mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge pull request #1607 from rtfeldman/editor-let-value
file loading/saving/running, headers, top level defs
This commit is contained in:
commit
e3a8d436cf
54 changed files with 6193 additions and 2959 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -26,6 +26,9 @@ editor/benches/resources/25000000_lines.roc
|
|||
editor/benches/resources/50000_lines.roc
|
||||
editor/benches/resources/500_lines.roc
|
||||
|
||||
# file editor creates when no arg is passed
|
||||
new-roc-project
|
||||
|
||||
# rust cache (sccache folder)
|
||||
sccache_dir
|
||||
|
||||
|
|
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -1215,6 +1215,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
|
@ -3567,6 +3573,7 @@ dependencies = [
|
|||
"confy",
|
||||
"copypasta",
|
||||
"env_logger 0.8.4",
|
||||
"fs_extra",
|
||||
"futures",
|
||||
"glyph_brush",
|
||||
"im 15.0.0",
|
||||
|
@ -3584,9 +3591,11 @@ dependencies = [
|
|||
"quickcheck 1.0.3",
|
||||
"quickcheck_macros 1.0.0",
|
||||
"rand 0.8.4",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_fmt",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
|
@ -3598,6 +3607,8 @@ dependencies = [
|
|||
"ropey",
|
||||
"serde",
|
||||
"snafu",
|
||||
"tempfile",
|
||||
"uuid",
|
||||
"ven_graph",
|
||||
"wgpu",
|
||||
"wgpu_glyph",
|
||||
|
@ -4758,6 +4769,15 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
|
|
|
@ -65,7 +65,7 @@ check-rustfmt:
|
|||
RUN cargo fmt --all -- --check
|
||||
|
||||
check-typos:
|
||||
RUN cargo install --version 1.0.11 typos-cli
|
||||
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
|
||||
COPY --dir .github ci cli compiler docs editor examples linker nightly_benches packages roc_std www *.md LEGAL_DETAILS shell.nix ./
|
||||
RUN typos
|
||||
|
||||
|
@ -97,7 +97,6 @@ test-all:
|
|||
BUILD +test-zig
|
||||
BUILD +check-rustfmt
|
||||
BUILD +check-clippy
|
||||
BUILD +check-typos
|
||||
BUILD +test-rust
|
||||
BUILD +verify-no-git-changes
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ fn main() -> io::Result<()> {
|
|||
}
|
||||
|
||||
None => {
|
||||
launch_editor(&[])?;
|
||||
launch_editor(None)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -91,16 +91,13 @@ If you're building the compiler from source you'll want to do `cargo run [FILE]`
|
|||
.subcommand_matches(CMD_EDIT)
|
||||
.unwrap()
|
||||
.values_of_os(DIRECTORY_OR_FILES)
|
||||
.map(|mut values| values.next())
|
||||
{
|
||||
None => {
|
||||
launch_editor(&[])?;
|
||||
Some(Some(os_str)) => {
|
||||
launch_editor(Some(Path::new(os_str)))?;
|
||||
}
|
||||
Some(values) => {
|
||||
let paths = values
|
||||
.map(|os_str| Path::new(os_str))
|
||||
.collect::<Vec<&Path>>();
|
||||
|
||||
launch_editor(&paths)?;
|
||||
_ => {
|
||||
launch_editor(None)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,8 +184,8 @@ fn roc_files_recursive<P: AsRef<Path>>(
|
|||
}
|
||||
|
||||
#[cfg(feature = "editor")]
|
||||
fn launch_editor(filepaths: &[&Path]) -> io::Result<()> {
|
||||
roc_editor::launch(filepaths)
|
||||
fn launch_editor(project_dir_path: Option<&Path>) -> io::Result<()> {
|
||||
roc_editor::launch(project_dir_path)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "editor"))]
|
||||
|
|
|
@ -1967,7 +1967,7 @@ fn update<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
if module_id == state.root_id && state.goal_phase == Phase::SolveTypes {
|
||||
if is_host_exposed && state.goal_phase == Phase::SolveTypes {
|
||||
debug_assert!(work.is_empty());
|
||||
debug_assert!(state.dependencies.solved_all());
|
||||
|
||||
|
|
|
@ -551,7 +551,53 @@ impl IdentIds {
|
|||
}
|
||||
}
|
||||
|
||||
/// Generates a unique, new name that's just a stringified integer
|
||||
// necessary when the name of a value is changed in the editor
|
||||
pub fn update_key(
|
||||
&mut self,
|
||||
old_ident_name: &str,
|
||||
new_ident_name: &str,
|
||||
) -> Result<IdentId, String> {
|
||||
let old_ident: Ident = old_ident_name.into();
|
||||
|
||||
let ident_id_ref_opt = self.by_ident.get(&old_ident);
|
||||
|
||||
match ident_id_ref_opt {
|
||||
Some(ident_id_ref) => {
|
||||
let ident_id = *ident_id_ref;
|
||||
|
||||
self.by_ident.remove(&old_ident);
|
||||
self.by_ident.insert(new_ident_name.into(), ident_id);
|
||||
|
||||
let by_id = &mut self.by_id;
|
||||
let key_index_opt = by_id.iter().position(|x| *x == old_ident);
|
||||
|
||||
if let Some(key_index) = key_index_opt {
|
||||
if let Some(vec_elt) = by_id.get_mut(key_index) {
|
||||
*vec_elt = new_ident_name.into();
|
||||
} else {
|
||||
// we get the index from by_id
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
Ok(ident_id)
|
||||
} else {
|
||||
Err(
|
||||
format!(
|
||||
"Tried to find position of key {:?} in IdentIds.by_id but I could not find the key. IdentIds.by_id: {:?}",
|
||||
old_ident_name,
|
||||
self.by_id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"Tried to update key in IdentIds ({:?}) but I could not find the key ({}).",
|
||||
self.by_ident, old_ident_name
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a unique, new name that's just a strigified integer
|
||||
/// (e.g. "1" or "5"), using an internal counter. Since valid Roc variable
|
||||
/// names cannot begin with a number, this has no chance of colliding
|
||||
/// with actual user-defined variables.
|
||||
|
|
|
@ -32,7 +32,7 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, SyntaxError<'a>> {
|
||||
// force that we pare until the end of the input
|
||||
// force that we parse until the end of the input
|
||||
let min_indent = 0;
|
||||
skip_second!(
|
||||
specialize(
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::ast;
|
||||
use crate::module::module_defs;
|
||||
// use crate::module::module_defs;
|
||||
use crate::parser::Parser;
|
||||
use crate::parser::{State, SyntaxError};
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
|
||||
|
@ -23,3 +26,15 @@ pub fn parse_loc_with<'a>(
|
|||
Err(fail) => Err(SyntaxError::Expr(fail)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_defs_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<BumpVec<'a, Located<ast::Def<'a>>>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
|
||||
match module_defs().parse(arena, state) {
|
||||
Ok(tuple) => Ok(tuple.1),
|
||||
Err(tuple) => Err(tuple.1),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ exclude = ["src/shaders/*.spv"]
|
|||
|
||||
[dependencies]
|
||||
roc_collections = { path = "../compiler/collections" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
roc_builtins = { path = "../compiler/builtins" }
|
||||
roc_can = { path = "../compiler/can" }
|
||||
roc_parse = { path = "../compiler/parse" }
|
||||
roc_region = { path = "../compiler/region" }
|
||||
|
@ -49,6 +51,9 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [
|
|||
], default-features = false }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
nonempty = "0.6.0"
|
||||
tempfile = "3.2.0"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
fs_extra = "1.2.0"
|
||||
|
||||
[dependencies.bytemuck]
|
||||
version = "1.4"
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::ui::text::lines::Lines;
|
||||
use crate::ui::text::selection::Selection;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
use crate::ui::ui_error::{LineInsertionFailed, OutOfBounds, UIResult};
|
||||
use crate::ui::util::slice_get;
|
||||
use crate::ui::util::slice_get_mut;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -14,31 +14,102 @@ pub struct CodeLines {
|
|||
}
|
||||
|
||||
impl CodeLines {
|
||||
pub fn from_str(code_str: &str) -> CodeLines {
|
||||
CodeLines {
|
||||
lines: code_str
|
||||
.split_inclusive('\n')
|
||||
.map(|s| s.to_owned())
|
||||
.collect(),
|
||||
nr_of_chars: code_str.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_between_line(
|
||||
&mut self,
|
||||
line_nr: usize,
|
||||
index: usize,
|
||||
new_str: &str,
|
||||
) -> UIResult<()> {
|
||||
let nr_of_lines = self.lines.len();
|
||||
|
||||
if line_nr < nr_of_lines {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
line_ref.insert_str(index, new_str);
|
||||
} else if line_nr >= self.lines.len() {
|
||||
for _ in 0..((line_nr - nr_of_lines) + 1) {
|
||||
self.push_empty_line();
|
||||
}
|
||||
|
||||
self.insert_between_line(line_nr, index, new_str)?;
|
||||
} else {
|
||||
LineInsertionFailed {
|
||||
line_nr,
|
||||
nr_of_lines,
|
||||
}
|
||||
.fail()?;
|
||||
}
|
||||
|
||||
self.nr_of_chars += new_str.len();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_empty_line(&mut self, line_nr: usize) -> UIResult<()> {
|
||||
if line_nr <= self.lines.len() {
|
||||
self.lines.insert(line_nr, String::new());
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
OutOfBounds {
|
||||
index: line_nr,
|
||||
collection_name: "code_lines.lines".to_owned(),
|
||||
len: self.lines.len(),
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_empty_line(&mut self) {
|
||||
self.lines.push(String::new())
|
||||
}
|
||||
|
||||
pub fn break_line(&mut self, line_nr: usize, col_nr: usize) -> UIResult<()> {
|
||||
// clippy prefers this over if-else
|
||||
match line_nr.cmp(&self.lines.len()) {
|
||||
Ordering::Less => {
|
||||
self.insert_empty_line(line_nr + 1)?;
|
||||
|
||||
let line_ref = self.lines.get_mut(line_nr).unwrap(); // safe because we checked line_nr
|
||||
|
||||
if col_nr < line_ref.len() {
|
||||
let next_line_str: String = line_ref.drain(col_nr..).collect();
|
||||
|
||||
let next_line_ref = self.lines.get_mut(line_nr + 1).unwrap(); // safe because we just added the line
|
||||
|
||||
*next_line_ref = next_line_str;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ordering::Equal => self.insert_empty_line(line_nr + 1),
|
||||
Ordering::Greater => OutOfBounds {
|
||||
index: line_nr,
|
||||
collection_name: "code_lines.lines".to_owned(),
|
||||
len: self.lines.len(),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_line(&mut self, line_nr: usize) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
*line_ref = String::new();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_line(&mut self, line_nr: usize) -> UIResult<()> {
|
||||
let line_len = self.line_len(line_nr)?;
|
||||
|
||||
self.lines.remove(line_nr);
|
||||
|
||||
self.nr_of_chars -= line_len;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_at_line(&mut self, line_nr: usize, index: usize) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
|
@ -49,6 +120,18 @@ impl CodeLines {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_range_at_line(
|
||||
&mut self,
|
||||
line_nr: usize,
|
||||
col_range: std::ops::Range<usize>,
|
||||
) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
line_ref.drain(col_range);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_selection(&mut self, selection: Selection) -> UIResult<()> {
|
||||
if selection.is_on_same_line() {
|
||||
let line_ref = slice_get_mut(selection.start_pos.line, &mut self.lines)?;
|
||||
|
@ -60,17 +143,36 @@ impl CodeLines {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// last column of last line
|
||||
pub fn end_txt_pos(&self) -> TextPos {
|
||||
let last_line_nr = self.nr_of_lines() - 1;
|
||||
|
||||
TextPos {
|
||||
line: last_line_nr,
|
||||
column: self.line_len(last_line_nr).unwrap(), // safe because we just calculated last_line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CodeLines {
|
||||
fn default() -> Self {
|
||||
CodeLines {
|
||||
lines: Vec::new(),
|
||||
nr_of_chars: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Lines for CodeLines {
|
||||
fn get_line(&self, line_nr: usize) -> UIResult<&str> {
|
||||
fn get_line_ref(&self, line_nr: usize) -> UIResult<&str> {
|
||||
let line_string = slice_get(line_nr, &self.lines)?;
|
||||
|
||||
Ok(line_string)
|
||||
}
|
||||
|
||||
fn line_len(&self, line_nr: usize) -> UIResult<usize> {
|
||||
self.get_line(line_nr).map(|line| line.len())
|
||||
self.get_line_ref(line_nr).map(|line| line.len())
|
||||
}
|
||||
|
||||
fn nr_of_lines(&self) -> usize {
|
||||
|
@ -81,14 +183,8 @@ impl Lines for CodeLines {
|
|||
self.nr_of_chars
|
||||
}
|
||||
|
||||
fn all_lines<'a>(&self, arena: &'a Bump) -> BumpString<'a> {
|
||||
let mut lines = BumpString::with_capacity_in(self.nr_of_chars(), arena);
|
||||
|
||||
for line in &self.lines {
|
||||
lines.push_str(line);
|
||||
}
|
||||
|
||||
lines
|
||||
fn all_lines_as_string(&self) -> String {
|
||||
self.lines.join("\n")
|
||||
}
|
||||
|
||||
fn is_last_line(&self, line_nr: usize) -> bool {
|
||||
|
@ -96,7 +192,7 @@ impl Lines for CodeLines {
|
|||
}
|
||||
|
||||
fn last_char(&self, line_nr: usize) -> UIResult<Option<char>> {
|
||||
Ok(self.get_line(line_nr)?.chars().last())
|
||||
Ok(self.get_line_ref(line_nr)?.chars().last())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,14 +201,16 @@ impl fmt::Display for CodeLines {
|
|||
for row in &self.lines {
|
||||
let row_str = row
|
||||
.chars()
|
||||
.map(|code_char| format!("'{}'", code_char))
|
||||
.map(|code_char| format!("{}", code_char))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
.join(" ");
|
||||
|
||||
write!(f, "\n{}", row_str)?;
|
||||
let escaped_row_str = row_str.replace("\n", "\\n");
|
||||
|
||||
write!(f, "\n{}", escaped_row_str)?;
|
||||
}
|
||||
|
||||
write!(f, " (code_lines)")?;
|
||||
writeln!(f, " (code_lines, {:?} lines)", self.lines.len())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::editor::theme::EdTheme;
|
|||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub code_font_size: f32,
|
||||
pub debug_font_size: f32,
|
||||
pub ed_theme: EdTheme,
|
||||
}
|
||||
|
||||
|
@ -12,6 +13,7 @@ impl Default for Config {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
code_font_size: 30.0,
|
||||
debug_font_size: 20.0,
|
||||
ed_theme: EdTheme::default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use crate::{editor::slow_pool::MarkNodeId, ui::text::text_pos::TextPos};
|
||||
use colored::*;
|
||||
|
@ -11,6 +12,24 @@ use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu};
|
|||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum EdError {
|
||||
#[snafu(display(
|
||||
"ASTNodeIdWithoutDefId: The expr_id_opt in ASTNode({:?}) was `None` but I was expexting `Some(DefId)` .",
|
||||
ast_node_id
|
||||
))]
|
||||
ASTNodeIdWithoutDefId {
|
||||
ast_node_id: ASTNodeId,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"ASTNodeIdWithoutExprId: The expr_id_opt in ASTNode({:?}) was `None` but I was expexting `Some(ExprId)` .",
|
||||
ast_node_id
|
||||
))]
|
||||
ASTNodeIdWithoutExprId {
|
||||
ast_node_id: ASTNodeId,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"CaretNotFound: No carets were found in the expected node with id {}",
|
||||
node_id
|
||||
|
@ -43,6 +62,17 @@ pub enum EdError {
|
|||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"EmptyCodeString: I need to have a code string (code_str) that contains either an app, interface or Package-Config header. The code string was empty.",
|
||||
))]
|
||||
EmptyCodeString { backtrace: Backtrace },
|
||||
|
||||
#[snafu(display("FailedToUpdateIdentIdName: {}", err_str))]
|
||||
FailedToUpdateIdentIdName {
|
||||
err_str: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))]
|
||||
GetContentOnNestedNode { backtrace: Backtrace },
|
||||
|
||||
|
@ -99,6 +129,12 @@ pub enum EdError {
|
|||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("NoDefMarkNodeBeforeLineNr: I could not find a MarkupNode whose root parent points to a DefId located before the given line number: {}.", line_nr))]
|
||||
NoDefMarkNodeBeforeLineNr {
|
||||
line_nr: usize,
|
||||
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 },
|
||||
|
||||
|
@ -131,6 +167,17 @@ pub enum EdError {
|
|||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"UnexpectedPattern2Variant: required a {} at this position, Pattern2 was a {}.",
|
||||
required_pattern2,
|
||||
encountered_pattern2,
|
||||
))]
|
||||
UnexpectedPattern2Variant {
|
||||
required_pattern2: String,
|
||||
encountered_pattern2: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"UnexpectedEmptyPoolVec: expected PoolVec {} to have at least one element.",
|
||||
descriptive_vec_name
|
||||
|
@ -154,7 +201,10 @@ pub enum EdError {
|
|||
},
|
||||
|
||||
#[snafu(display("ParseError: Failed to parse AST: SyntaxError: {}.", syntax_err))]
|
||||
ParseError { syntax_err: String },
|
||||
SrcParseError {
|
||||
syntax_err: String,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmptyRecord."))]
|
||||
RecordWithoutFields { backtrace: Backtrace },
|
||||
|
@ -176,14 +226,6 @@ pub fn print_err(err: &EdError) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_ui_err(err: &UIError) {
|
||||
eprintln!("{}", format!("{}", err).truecolor(255, 0, 0));
|
||||
|
||||
if let Some(backtrace) = ErrorCompat::backtrace(err) {
|
||||
eprintln!("{}", color_backtrace(backtrace));
|
||||
}
|
||||
}
|
||||
|
||||
fn color_backtrace(backtrace: &snafu::Backtrace) -> String {
|
||||
let backtrace_str = format!("{}", backtrace);
|
||||
let backtrace_split = backtrace_str.split('\n');
|
||||
|
|
|
@ -1,40 +1,28 @@
|
|||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::ed_error::NestedNodeWithoutChildren;
|
||||
use crate::editor::ed_error::NodeIdNotInGridNodeMap;
|
||||
use crate::editor::ed_error::{NoDefMarkNodeBeforeLineNr, NodeIdNotInGridNodeMap};
|
||||
use crate::editor::mvc::ed_model::EdModel;
|
||||
use crate::editor::slow_pool::MarkNodeId;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::editor::util::first_last_index_of;
|
||||
use crate::editor::util::index_of;
|
||||
use crate::lang::ast::ExprId;
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::ui::text::selection::Selection;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use crate::ui::ui_error::{LineInsertionFailed, OutOfBounds, UIResult};
|
||||
use crate::ui::util::{slice_get, slice_get_mut};
|
||||
use snafu::OptionExt;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
use super::markup::nodes::get_root_mark_node_id;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridNodeMap {
|
||||
pub lines: Vec<Vec<MarkNodeId>>,
|
||||
}
|
||||
|
||||
impl GridNodeMap {
|
||||
pub fn new() -> GridNodeMap {
|
||||
GridNodeMap {
|
||||
lines: vec![vec![]],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_line(&mut self, line_nr: usize, len: usize, node_id: MarkNodeId) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
let mut new_cols_vec: Vec<MarkNodeId> = std::iter::repeat(node_id).take(len).collect();
|
||||
|
||||
line_ref.append(&mut new_cols_vec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_between_line(
|
||||
&mut self,
|
||||
line_nr: usize,
|
||||
|
@ -42,18 +30,105 @@ impl GridNodeMap {
|
|||
len: usize,
|
||||
node_id: MarkNodeId,
|
||||
) -> UIResult<()> {
|
||||
let nr_of_lines = self.lines.len();
|
||||
|
||||
if line_nr < nr_of_lines {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
let new_cols_vec: Vec<MarkNodeId> = std::iter::repeat(node_id).take(len).collect();
|
||||
|
||||
line_ref.splice(index..index, new_cols_vec);
|
||||
} else if line_nr >= nr_of_lines {
|
||||
for _ in 0..((line_nr - nr_of_lines) + 1) {
|
||||
self.push_empty_line();
|
||||
}
|
||||
|
||||
self.insert_between_line(line_nr, index, len, node_id)?;
|
||||
} else {
|
||||
LineInsertionFailed {
|
||||
line_nr,
|
||||
nr_of_lines,
|
||||
}
|
||||
.fail()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_at_line(&mut self, line_nr: usize, index: usize) -> UIResult<()> {
|
||||
pub fn insert_empty_line(&mut self, line_nr: usize) -> UIResult<()> {
|
||||
if line_nr <= self.lines.len() {
|
||||
self.lines.insert(line_nr, Vec::new());
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
OutOfBounds {
|
||||
index: line_nr,
|
||||
collection_name: "code_lines.lines".to_owned(),
|
||||
len: self.lines.len(),
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_empty_line(&mut self) {
|
||||
self.lines.push(vec![]);
|
||||
}
|
||||
|
||||
pub fn break_line(&mut self, line_nr: usize, col_nr: usize) -> UIResult<()> {
|
||||
// clippy prefers this over if-else
|
||||
match line_nr.cmp(&self.lines.len()) {
|
||||
Ordering::Less => {
|
||||
self.insert_empty_line(line_nr + 1)?;
|
||||
|
||||
let line_ref = self.lines.get_mut(line_nr).unwrap(); // safe because we checked line_nr
|
||||
|
||||
if col_nr < line_ref.len() {
|
||||
let next_line_str: Vec<MarkNodeId> = line_ref.drain(col_nr..).collect();
|
||||
|
||||
let next_line_ref = self.lines.get_mut(line_nr + 1).unwrap(); // safe because we just added the line
|
||||
|
||||
*next_line_ref = next_line_str;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ordering::Equal => self.insert_empty_line(line_nr + 1),
|
||||
Ordering::Greater => OutOfBounds {
|
||||
index: line_nr,
|
||||
collection_name: "grid_node_map.lines".to_owned(),
|
||||
len: self.lines.len(),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_line(&mut self, line_nr: usize) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
line_ref.remove(index);
|
||||
*line_ref = vec![];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_line(&mut self, line_nr: usize) {
|
||||
self.lines.remove(line_nr);
|
||||
}
|
||||
|
||||
pub fn del_at_line(&mut self, line_nr: usize, column: usize) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
line_ref.remove(column);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_range_at_line(
|
||||
&mut self,
|
||||
line_nr: usize,
|
||||
col_range: std::ops::Range<usize>,
|
||||
) -> UIResult<()> {
|
||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||
|
||||
line_ref.drain(col_range);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -64,16 +139,12 @@ impl GridNodeMap {
|
|||
|
||||
line_ref.drain(selection.start_pos.column..selection.end_pos.column);
|
||||
} else {
|
||||
// TODO support multiline
|
||||
unimplemented!("TODO support deleting multiline selection")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*pub fn new_line(&mut self) {
|
||||
self.lines.push(vec![])
|
||||
}*/
|
||||
|
||||
pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult<MarkNodeId> {
|
||||
let line = slice_get(caret_pos.line, &self.lines)?;
|
||||
let node_id = slice_get(caret_pos.column, line)?;
|
||||
|
@ -133,15 +204,15 @@ impl GridNodeMap {
|
|||
}
|
||||
}
|
||||
|
||||
// returns start and end pos of Expr2, relevant AST node and MarkNodeId of the corresponding MarkupNode
|
||||
pub fn get_expr_start_end_pos(
|
||||
// returns start and end pos of Expr2/Def2, relevant AST node and MarkNodeId of the corresponding MarkupNode
|
||||
pub fn get_block_start_end_pos(
|
||||
&self,
|
||||
caret_pos: TextPos,
|
||||
ed_model: &EdModel,
|
||||
) -> EdResult<(TextPos, TextPos, ExprId, MarkNodeId)> {
|
||||
) -> EdResult<(TextPos, TextPos, ASTNodeId, MarkNodeId)> {
|
||||
let line = slice_get(caret_pos.line, &self.lines)?;
|
||||
let node_id = slice_get(caret_pos.column, line)?;
|
||||
let node = ed_model.markup_node_pool.get(*node_id);
|
||||
let node = ed_model.mark_node_pool.get(*node_id);
|
||||
|
||||
if node.is_nested() {
|
||||
let (start_pos, end_pos) = self.get_nested_start_end_pos(*node_id, ed_model)?;
|
||||
|
@ -151,10 +222,7 @@ impl GridNodeMap {
|
|||
let (first_node_index, last_node_index) = first_last_index_of(*node_id, line)?;
|
||||
|
||||
let curr_node_id = slice_get(first_node_index, line)?;
|
||||
let curr_ast_node_id = ed_model
|
||||
.markup_node_pool
|
||||
.get(*curr_node_id)
|
||||
.get_ast_node_id();
|
||||
let curr_ast_node_id = ed_model.mark_node_pool.get(*curr_node_id).get_ast_node_id();
|
||||
|
||||
let mut expr_start_index = first_node_index;
|
||||
let mut expr_end_index = last_node_index;
|
||||
|
@ -165,7 +233,7 @@ impl GridNodeMap {
|
|||
for i in (0..first_node_index).rev() {
|
||||
let prev_pos_node_id = slice_get(i, line)?;
|
||||
let prev_ast_node_id = ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.get(*prev_pos_node_id)
|
||||
.get_ast_node_id();
|
||||
|
||||
|
@ -187,7 +255,7 @@ impl GridNodeMap {
|
|||
for i in last_node_index..line.len() {
|
||||
let next_pos_node_id = slice_get(i, line)?;
|
||||
let next_ast_node_id = ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.get(*next_pos_node_id)
|
||||
.get_ast_node_id();
|
||||
|
||||
|
@ -204,7 +272,7 @@ impl GridNodeMap {
|
|||
}
|
||||
|
||||
let correct_mark_node_id =
|
||||
GridNodeMap::get_top_node_with_expr_id(*curr_node_id, &ed_model.markup_node_pool);
|
||||
GridNodeMap::get_top_node_with_expr_id(*curr_node_id, &ed_model.mark_node_pool);
|
||||
|
||||
Ok((
|
||||
TextPos {
|
||||
|
@ -225,12 +293,12 @@ impl GridNodeMap {
|
|||
// `{` is not the entire Expr2
|
||||
fn get_top_node_with_expr_id(
|
||||
curr_node_id: MarkNodeId,
|
||||
markup_node_pool: &SlowPool,
|
||||
mark_node_pool: &SlowPool,
|
||||
) -> MarkNodeId {
|
||||
let curr_node = markup_node_pool.get(curr_node_id);
|
||||
let curr_node = mark_node_pool.get(curr_node_id);
|
||||
|
||||
if let Some(parent_id) = curr_node.get_parent_id_opt() {
|
||||
let parent = markup_node_pool.get(parent_id);
|
||||
let parent = mark_node_pool.get(parent_id);
|
||||
|
||||
if parent.get_ast_node_id() == curr_node.get_ast_node_id() {
|
||||
parent_id
|
||||
|
@ -247,29 +315,108 @@ impl GridNodeMap {
|
|||
nested_node_id: MarkNodeId,
|
||||
ed_model: &EdModel,
|
||||
) -> EdResult<(TextPos, TextPos)> {
|
||||
let parent_mark_node = ed_model.markup_node_pool.get(nested_node_id);
|
||||
let left_most_leaf = self.get_leftmost_leaf(nested_node_id, ed_model)?;
|
||||
|
||||
let all_child_ids = parent_mark_node.get_children_ids();
|
||||
let first_child_id = all_child_ids
|
||||
let right_most_leaf = self.get_rightmost_leaf(nested_node_id, ed_model)?;
|
||||
|
||||
let expr_start_pos = ed_model
|
||||
.grid_node_map
|
||||
.get_node_position(left_most_leaf, true)?;
|
||||
let expr_end_pos = ed_model
|
||||
.grid_node_map
|
||||
.get_node_position(right_most_leaf, false)?
|
||||
.increment_col();
|
||||
|
||||
Ok((expr_start_pos, expr_end_pos))
|
||||
}
|
||||
|
||||
fn get_leftmost_leaf(
|
||||
&self,
|
||||
nested_node_id: MarkNodeId,
|
||||
ed_model: &EdModel,
|
||||
) -> EdResult<MarkNodeId> {
|
||||
let mut children_ids = ed_model
|
||||
.mark_node_pool
|
||||
.get(nested_node_id)
|
||||
.get_children_ids();
|
||||
let mut first_child_id = 0;
|
||||
|
||||
while !children_ids.is_empty() {
|
||||
first_child_id = *children_ids
|
||||
.first()
|
||||
.with_context(|| NestedNodeWithoutChildren {
|
||||
node_id: nested_node_id,
|
||||
})?;
|
||||
let last_child_id = all_child_ids
|
||||
|
||||
children_ids = ed_model
|
||||
.mark_node_pool
|
||||
.get(first_child_id)
|
||||
.get_children_ids();
|
||||
}
|
||||
|
||||
Ok(first_child_id)
|
||||
}
|
||||
|
||||
fn get_rightmost_leaf(
|
||||
&self,
|
||||
nested_node_id: MarkNodeId,
|
||||
ed_model: &EdModel,
|
||||
) -> EdResult<MarkNodeId> {
|
||||
let mut children_ids = ed_model
|
||||
.mark_node_pool
|
||||
.get(nested_node_id)
|
||||
.get_children_ids();
|
||||
let mut last_child_id = 0;
|
||||
|
||||
while !children_ids.is_empty() {
|
||||
last_child_id = *children_ids
|
||||
.last()
|
||||
.with_context(|| NestedNodeWithoutChildren {
|
||||
node_id: nested_node_id,
|
||||
})?;
|
||||
|
||||
let expr_start_pos = ed_model
|
||||
.grid_node_map
|
||||
.get_node_position(*first_child_id, true)?;
|
||||
let expr_end_pos = ed_model
|
||||
.grid_node_map
|
||||
.get_node_position(*last_child_id, false)?
|
||||
.increment_col();
|
||||
children_ids = ed_model
|
||||
.mark_node_pool
|
||||
.get(last_child_id)
|
||||
.get_children_ids();
|
||||
}
|
||||
|
||||
Ok((expr_start_pos, expr_end_pos))
|
||||
Ok(last_child_id)
|
||||
}
|
||||
|
||||
// get id of root mark_node whose ast_node_id points to a DefId
|
||||
pub fn get_def_mark_node_id_before_line(
|
||||
&self,
|
||||
line_nr: usize,
|
||||
mark_node_pool: &SlowPool,
|
||||
) -> EdResult<MarkNodeId> {
|
||||
for curr_line_nr in (0..line_nr).rev() {
|
||||
let first_col_pos = TextPos {
|
||||
line: curr_line_nr,
|
||||
column: 0,
|
||||
};
|
||||
|
||||
if self.node_exists_at_pos(first_col_pos) {
|
||||
let mark_node_id = self.get_id_at_row_col(first_col_pos)?;
|
||||
let root_mark_node_id = get_root_mark_node_id(mark_node_id, mark_node_pool);
|
||||
|
||||
let ast_node_id = mark_node_pool.get(root_mark_node_id).get_ast_node_id();
|
||||
|
||||
if let ASTNodeId::ADefId(_) = ast_node_id {
|
||||
return Ok(root_mark_node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NoDefMarkNodeBeforeLineNr { line_nr }.fail()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GridNodeMap {
|
||||
fn default() -> Self {
|
||||
GridNodeMap {
|
||||
lines: vec![Vec::new()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,10 +429,10 @@ impl fmt::Display for GridNodeMap {
|
|||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
write!(f, "{}", row_str)?;
|
||||
writeln!(f, "{}", row_str)?;
|
||||
}
|
||||
|
||||
write!(f, " (grid_node_map)")?;
|
||||
writeln!(f, "(grid_node_map, {:?} lines)", self.lines.len())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn handle_keydown(
|
|||
}
|
||||
}
|
||||
|
||||
A | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||
A | S | R | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||
|
||||
F11 => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use super::keyboard_input;
|
||||
use super::style::CODE_TXT_XY;
|
||||
use crate::editor::ed_error::print_ui_err;
|
||||
use crate::editor::mvc::ed_view;
|
||||
use crate::editor::mvc::ed_view::RenderedWgpu;
|
||||
use crate::editor::resources::strings::NOTHING_OPENED;
|
||||
use crate::editor::resources::strings::{HELLO_WORLD, NOTHING_OPENED};
|
||||
use crate::editor::{
|
||||
config::Config,
|
||||
ed_error::print_err,
|
||||
|
@ -20,17 +19,23 @@ use crate::graphics::{
|
|||
};
|
||||
use crate::lang::expr::Env;
|
||||
use crate::lang::pool::Pool;
|
||||
use crate::ui::ui_error::UIError::FileOpenFailed;
|
||||
use crate::ui::util::slice_get;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use crate::ui::text::caret_w_select::CaretPos;
|
||||
use crate::ui::util::path_to_string;
|
||||
use bumpalo::Bump;
|
||||
use cgmath::Vector2;
|
||||
use fs_extra::dir::{copy, ls, CopyOptions, DirEntryAttr, DirEntryValue};
|
||||
use pipelines::RectResources;
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_module::symbol::{IdentIds, ModuleIds};
|
||||
use roc_can::builtins::builtin_defs_map;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_load;
|
||||
use roc_load::file::LoadedModule;
|
||||
use roc_module::symbol::IdentIds;
|
||||
use roc_types::subs::VarStore;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::{error::Error, io, path::Path};
|
||||
use wgpu::{CommandEncoder, RenderPass, TextureView};
|
||||
use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView};
|
||||
use wgpu_glyph::GlyphBrush;
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
|
@ -48,26 +53,13 @@ use winit::{
|
|||
|
||||
/// The editor is actually launched from the CLI if you pass it zero arguments,
|
||||
/// or if you provide it 1 or more files or directories to open on launch.
|
||||
pub fn launch(filepaths: &[&Path]) -> io::Result<()> {
|
||||
//TODO support using multiple filepaths
|
||||
let first_path_opt = if !filepaths.is_empty() {
|
||||
match slice_get(0, filepaths) {
|
||||
Ok(path_ref_ref) => Some(*path_ref_ref),
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
run_event_loop(first_path_opt).expect("Error running event loop");
|
||||
pub fn launch(project_dir_path_opt: Option<&Path>) -> io::Result<()> {
|
||||
run_event_loop(project_dir_path_opt).expect("Error running event loop");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
|
||||
// Open window and create a surface
|
||||
|
@ -134,51 +126,37 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
let env_arena = Bump::new();
|
||||
let code_arena = Bump::new();
|
||||
|
||||
let (file_path_str, code_str) = read_main_roc_file(project_dir_path_opt);
|
||||
println!("Loading file {}...", file_path_str);
|
||||
|
||||
let file_path = Path::new(&file_path_str);
|
||||
|
||||
let loaded_module = load_module(file_path);
|
||||
|
||||
let mut var_store = VarStore::default();
|
||||
let dep_idents = IdentIds::exposed_builtins(8);
|
||||
|
||||
let exposed_ident_ids = IdentIds::default();
|
||||
let mut module_ids = ModuleIds::default();
|
||||
let mod_id = module_ids.get_or_insert(&"ModId123".into());
|
||||
|
||||
let interns = Interns {
|
||||
module_ids,
|
||||
all_ident_ids: IdentIds::exposed_builtins(8),
|
||||
};
|
||||
let module_ids = loaded_module.interns.module_ids.clone();
|
||||
|
||||
let env = Env::new(
|
||||
mod_id,
|
||||
loaded_module.module_id,
|
||||
&env_arena,
|
||||
&mut env_pool,
|
||||
&mut var_store,
|
||||
dep_idents,
|
||||
&interns.module_ids,
|
||||
&module_ids,
|
||||
exposed_ident_ids,
|
||||
);
|
||||
|
||||
let mut code_str = BumpString::from_str_in("", &code_arena);
|
||||
|
||||
let file_path = if let Some(file_path) = file_path_opt {
|
||||
match std::fs::read_to_string(file_path) {
|
||||
Ok(file_as_str) => {
|
||||
code_str = BumpString::from_str_in(&file_as_str, &code_arena);
|
||||
file_path
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
print_ui_err(&FileOpenFailed {
|
||||
path_str: file_path.to_string_lossy().to_string(),
|
||||
err_msg: e.to_string(),
|
||||
});
|
||||
Path::new("")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Path::new("")
|
||||
};
|
||||
|
||||
let ed_model_opt = {
|
||||
let ed_model_res = ed_model::init_model(&code_str, file_path, env, &interns, &code_arena);
|
||||
let ed_model_res = ed_model::init_model(
|
||||
&code_str,
|
||||
file_path,
|
||||
env,
|
||||
loaded_module,
|
||||
&code_arena,
|
||||
CaretPos::End,
|
||||
);
|
||||
|
||||
match ed_model_res {
|
||||
Ok(mut ed_model) => {
|
||||
|
@ -251,7 +229,8 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
event: event::WindowEvent::ReceivedCharacter(ch),
|
||||
..
|
||||
} => {
|
||||
let input_outcome_res = app_update::handle_new_char(&ch, &mut app_model);
|
||||
let input_outcome_res =
|
||||
app_update::handle_new_char(&ch, &mut app_model, keyboard_modifiers);
|
||||
if let Err(e) = input_outcome_res {
|
||||
print_err(&e)
|
||||
} else if let Ok(InputOutcome::Ignored) = input_outcome_res {
|
||||
|
@ -314,23 +293,55 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
|
||||
if let Some(ref rendered_wgpu) = rendered_wgpu_opt {
|
||||
for text_section in &rendered_wgpu.text_sections {
|
||||
draw_rects(
|
||||
&rendered_wgpu.rects_behind,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
&gpu_device,
|
||||
&rect_resources,
|
||||
wgpu::LoadOp::Clear(to_wgpu_color(ed_theme.background)),
|
||||
);
|
||||
|
||||
for text_section in &rendered_wgpu.text_sections_behind {
|
||||
let borrowed_text = text_section.to_borrowed();
|
||||
|
||||
glyph_brush.queue(borrowed_text);
|
||||
}
|
||||
|
||||
draw_all_rects(
|
||||
&rendered_wgpu.rects,
|
||||
// draw first layer of text
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&gpu_device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Failed to draw first layer of text.");
|
||||
|
||||
// draw rects on top of first text layer
|
||||
draw_rects(
|
||||
&rendered_wgpu.rects_front,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
&gpu_device,
|
||||
&rect_resources,
|
||||
&ed_theme,
|
||||
)
|
||||
wgpu::LoadOp::Load,
|
||||
);
|
||||
|
||||
for text_section in &rendered_wgpu.text_sections_front {
|
||||
let borrowed_text = text_section.to_borrowed();
|
||||
|
||||
glyph_brush.queue(borrowed_text);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
begin_render_pass(&mut encoder, &frame.view, &ed_theme);
|
||||
begin_render_pass(
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
wgpu::LoadOp::Clear(to_wgpu_color(ed_theme.background)),
|
||||
);
|
||||
|
||||
queue_no_file_text(
|
||||
&size,
|
||||
|
@ -341,7 +352,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
);
|
||||
}
|
||||
|
||||
// draw all text
|
||||
// draw text
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&gpu_device,
|
||||
|
@ -351,7 +362,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Draw queued");
|
||||
.expect("Failed to draw queued text.");
|
||||
|
||||
staging_belt.finish();
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
|
@ -374,17 +385,17 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_all_rects(
|
||||
fn draw_rects(
|
||||
all_rects: &[Rect],
|
||||
encoder: &mut CommandEncoder,
|
||||
texture_view: &TextureView,
|
||||
gpu_device: &wgpu::Device,
|
||||
rect_resources: &RectResources,
|
||||
ed_theme: &EdTheme,
|
||||
load_op: LoadOp<wgpu::Color>,
|
||||
) {
|
||||
let rect_buffers = create_rect_buffers(gpu_device, encoder, all_rects);
|
||||
|
||||
let mut render_pass = begin_render_pass(encoder, texture_view, ed_theme);
|
||||
let mut render_pass = begin_render_pass(encoder, texture_view, load_op);
|
||||
|
||||
render_pass.set_pipeline(&rect_resources.pipeline);
|
||||
render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]);
|
||||
|
@ -399,16 +410,14 @@ fn draw_all_rects(
|
|||
fn begin_render_pass<'a>(
|
||||
encoder: &'a mut CommandEncoder,
|
||||
texture_view: &'a TextureView,
|
||||
ed_theme: &EdTheme,
|
||||
load_op: LoadOp<wgpu::Color>,
|
||||
) -> RenderPass<'a> {
|
||||
let bg_color = to_wgpu_color(ed_theme.background);
|
||||
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
view: texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(bg_color),
|
||||
load: load_op,
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
|
@ -417,6 +426,166 @@ fn begin_render_pass<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
type PathStr = String;
|
||||
|
||||
fn read_main_roc_file(project_dir_path_opt: Option<&Path>) -> (PathStr, String) {
|
||||
if let Some(project_dir_path) = project_dir_path_opt {
|
||||
let mut ls_config = HashSet::new();
|
||||
ls_config.insert(DirEntryAttr::FullName);
|
||||
|
||||
let dir_items = ls(project_dir_path, &ls_config)
|
||||
.unwrap_or_else(|err| panic!("Failed to list items in project directory: {:?}", err))
|
||||
.items;
|
||||
|
||||
let file_names = dir_items
|
||||
.iter()
|
||||
.map(|info_hash_map| {
|
||||
info_hash_map
|
||||
.values()
|
||||
.map(|dir_entry_value| {
|
||||
if let DirEntryValue::String(file_name) = dir_entry_value {
|
||||
Some(file_name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten() // remove None
|
||||
.collect::<Vec<&String>>()
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let roc_file_names: Vec<&String> = file_names
|
||||
.filter(|file_name| file_name.contains(".roc"))
|
||||
.collect();
|
||||
|
||||
let project_dir_path_str = path_to_string(project_dir_path);
|
||||
|
||||
if let Some(&roc_file_name) = roc_file_names.first() {
|
||||
let full_roc_file_path_str = vec![
|
||||
project_dir_path_str.clone(),
|
||||
"/".to_owned(),
|
||||
roc_file_name.clone(),
|
||||
]
|
||||
.join("");
|
||||
let file_as_str = std::fs::read_to_string(&Path::new(&full_roc_file_path_str))
|
||||
.unwrap_or_else(|err| panic!("In the provided project {:?}, I found the roc file {}, but I failed to read it: {}", &project_dir_path_str, &full_roc_file_path_str, err));
|
||||
|
||||
(full_roc_file_path_str, file_as_str)
|
||||
} else {
|
||||
init_new_roc_project(&project_dir_path_str)
|
||||
}
|
||||
} else {
|
||||
init_new_roc_project("new-roc-project")
|
||||
}
|
||||
}
|
||||
|
||||
// returns path and content of app file
|
||||
fn init_new_roc_project(project_dir_path_str: &str) -> (PathStr, String) {
|
||||
let orig_platform_path = Path::new("./examples/hello-world/platform");
|
||||
|
||||
let project_dir_path = Path::new(project_dir_path_str);
|
||||
|
||||
let roc_file_path_str = vec![project_dir_path_str, "/UntitledApp.roc"].join("");
|
||||
let roc_file_path = Path::new("./new-roc-project/UntitledApp.roc");
|
||||
|
||||
let project_platform_path_str = vec![project_dir_path_str, "/platform"].join("");
|
||||
let project_platform_path = Path::new(&project_platform_path_str);
|
||||
|
||||
if !project_dir_path.exists() {
|
||||
fs::create_dir(project_dir_path).expect("Failed to create dir for roc project.");
|
||||
}
|
||||
|
||||
copy_roc_platform_if_not_exists(orig_platform_path, project_platform_path, project_dir_path);
|
||||
|
||||
let code_str = create_roc_file_if_not_exists(project_dir_path, roc_file_path);
|
||||
|
||||
(roc_file_path_str, code_str)
|
||||
}
|
||||
|
||||
// returns contents of file
|
||||
fn create_roc_file_if_not_exists(project_dir_path: &Path, roc_file_path: &Path) -> String {
|
||||
if !roc_file_path.exists() {
|
||||
let mut roc_file = File::create(roc_file_path).unwrap_or_else(|err| {
|
||||
panic!("No roc file path was passed to the editor, so I wanted to create a new roc project with the file {:?}, but it failed: {}", roc_file_path, err)
|
||||
});
|
||||
|
||||
write!(roc_file, "{}", HELLO_WORLD).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
r#"No roc file path was passed to the editor, so I created a new roc project with the file {:?}
|
||||
I wanted to write roc hello world to that file, but it failed: {:?}"#,
|
||||
roc_file_path,
|
||||
err
|
||||
)
|
||||
});
|
||||
|
||||
HELLO_WORLD.to_string()
|
||||
} else {
|
||||
std::fs::read_to_string(roc_file_path).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"I detected an existing {:?} inside {:?}, but I failed to read from it: {}",
|
||||
roc_file_path, project_dir_path, err
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_roc_platform_if_not_exists(
|
||||
orig_platform_path: &Path,
|
||||
project_platform_path: &Path,
|
||||
project_dir_path: &Path,
|
||||
) {
|
||||
if !orig_platform_path.exists() && !project_platform_path.exists() {
|
||||
panic!(
|
||||
r#"No roc file path was passed to the editor, I wanted to create a new roc project but I could not find the platform at {:?}.
|
||||
Are you at the root of the roc repository?"#,
|
||||
orig_platform_path
|
||||
);
|
||||
} else if !project_platform_path.exists() {
|
||||
copy(orig_platform_path, project_dir_path, &CopyOptions::new()).unwrap_or_else(|err|{
|
||||
panic!(r#"No roc file path was passed to the editor, so I wanted to create a new roc project and roc projects require a platform,
|
||||
I tried to copy the platform at {:?} to {:?} but it failed: {}"#,
|
||||
orig_platform_path,
|
||||
project_platform_path,
|
||||
err
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_module(src_file: &Path) -> LoadedModule {
|
||||
let subs_by_module = MutMap::default();
|
||||
|
||||
let arena = Bump::new();
|
||||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
src_file.to_path_buf(),
|
||||
arena.alloc(roc_builtins::std::standard_stdlib()),
|
||||
src_file.parent().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"src_file {:?} did not have a parent directory but I need to have one.",
|
||||
src_file
|
||||
)
|
||||
}),
|
||||
subs_by_module,
|
||||
8,
|
||||
builtin_defs_map,
|
||||
);
|
||||
|
||||
match loaded {
|
||||
Ok(x) => x,
|
||||
Err(roc_load::file::LoadingProblem::FormattedReport(report)) => {
|
||||
panic!(
|
||||
"Failed to load module from src_file {:?}. Report: {:?}",
|
||||
src_file, report
|
||||
);
|
||||
}
|
||||
Err(e) => panic!(
|
||||
"Failed to load module from src_file {:?}: {:?}",
|
||||
src_file, e
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_no_file_text(
|
||||
size: &PhysicalSize<u32>,
|
||||
text: &str,
|
||||
|
|
107
editor/src/editor/markup/common_nodes.rs
Normal file
107
editor/src/editor/markup/common_nodes.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use crate::{
|
||||
editor::{slow_pool::MarkNodeId, syntax_highlight::HighlightStyle},
|
||||
lang::{ast::ExprId, parse::ASTNodeId},
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod attribute;
|
||||
pub mod common_nodes;
|
||||
pub mod nodes;
|
||||
|
|
|
@ -1,42 +1,62 @@
|
|||
use super::attribute::Attributes;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::ed_error::ExpectedTextNode;
|
||||
use crate::editor::ed_error::GetContentOnNestedNode;
|
||||
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::{Expr2, ExprId, RecordField};
|
||||
use crate::lang::{expr::Env, pool::PoolStr};
|
||||
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_module::symbol::Interns;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MarkupNode {
|
||||
Nested {
|
||||
ast_node_id: ExprId,
|
||||
ast_node_id: ASTNodeId,
|
||||
children_ids: Vec<MarkNodeId>,
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
newlines_at_end: usize,
|
||||
},
|
||||
Text {
|
||||
content: String,
|
||||
ast_node_id: ExprId,
|
||||
ast_node_id: ASTNodeId,
|
||||
syn_high_style: HighlightStyle,
|
||||
attributes: Attributes,
|
||||
parent_id_opt: Option<MarkNodeId>,
|
||||
newlines_at_end: usize,
|
||||
},
|
||||
Blank {
|
||||
ast_node_id: ExprId,
|
||||
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) -> ExprId {
|
||||
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,
|
||||
|
@ -60,9 +80,9 @@ impl MarkupNode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_sibling_ids(&self, markup_node_pool: &SlowPool) -> Vec<MarkNodeId> {
|
||||
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 = markup_node_pool.get(parent_id);
|
||||
let parent_node = mark_node_pool.get(parent_id);
|
||||
|
||||
parent_node.get_children_ids()
|
||||
} else {
|
||||
|
@ -74,7 +94,7 @@ impl MarkupNode {
|
|||
pub fn get_child_indices(
|
||||
&self,
|
||||
child_id: MarkNodeId,
|
||||
markup_node_pool: &SlowPool,
|
||||
mark_node_pool: &SlowPool,
|
||||
) -> EdResult<(usize, usize)> {
|
||||
match self {
|
||||
MarkupNode::Nested { children_ids, .. } => {
|
||||
|
@ -87,7 +107,7 @@ impl MarkupNode {
|
|||
mark_child_index_opt = Some(indx);
|
||||
}
|
||||
|
||||
let child_mark_node = markup_node_pool.get(mark_child_id);
|
||||
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 {
|
||||
|
@ -147,15 +167,25 @@ impl MarkupNode {
|
|||
}
|
||||
}
|
||||
|
||||
// can't be &str, this creates borrowing issues
|
||||
pub fn get_content(&self) -> EdResult<String> {
|
||||
pub fn get_content(&self) -> String {
|
||||
match self {
|
||||
MarkupNode::Nested { .. } => GetContentOnNestedNode {}.fail(),
|
||||
MarkupNode::Text { content, .. } => Ok(content.clone()),
|
||||
MarkupNode::Blank { .. } => Ok(BLANK_PLACEHOLDER.to_owned()),
|
||||
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) -> EdResult<&mut String> {
|
||||
match self {
|
||||
MarkupNode::Nested { .. } => ExpectedTextNode {
|
||||
|
@ -172,11 +202,10 @@ impl MarkupNode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_all_alphanumeric(&self) -> EdResult<bool> {
|
||||
Ok(self
|
||||
.get_content()?
|
||||
pub fn is_all_alphanumeric(&self) -> bool {
|
||||
self.get_content()
|
||||
.chars()
|
||||
.all(|chr| chr.is_ascii_alphanumeric()))
|
||||
.all(|chr| chr.is_ascii_alphanumeric())
|
||||
}
|
||||
|
||||
pub fn add_child_at_index(&mut self, index: usize, child_id: MarkNodeId) -> EdResult<()> {
|
||||
|
@ -209,6 +238,34 @@ impl MarkupNode {
|
|||
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 {
|
||||
|
@ -223,12 +280,13 @@ 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: ExprId,
|
||||
node_id: ASTNodeId,
|
||||
highlight_style: HighlightStyle,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) -> MarkNodeId {
|
||||
let node = MarkupNode::Text {
|
||||
content: text,
|
||||
|
@ -236,9 +294,51 @@ fn new_markup_node(
|
|||
syn_high_style: highlight_style,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
markup_node_pool.add(node)
|
||||
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,
|
||||
) -> EdResult<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
|
||||
|
@ -247,55 +347,44 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
env: &mut Env<'b>,
|
||||
expr2: &Expr2,
|
||||
expr2_node_id: ExprId,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
) -> MarkNodeId {
|
||||
match expr2 {
|
||||
mark_node_pool: &mut SlowPool,
|
||||
interns: &Interns,
|
||||
) -> EdResult<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,
|
||||
expr2_node_id,
|
||||
HighlightStyle::Number,
|
||||
markup_node_pool,
|
||||
)
|
||||
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) + "\"",
|
||||
expr2_node_id,
|
||||
ast_node_id,
|
||||
HighlightStyle::String,
|
||||
markup_node_pool,
|
||||
mark_node_pool,
|
||||
),
|
||||
Expr2::GlobalTag { name, .. } => new_markup_node(
|
||||
get_string(env, name),
|
||||
expr2_node_id,
|
||||
ast_node_id,
|
||||
HighlightStyle::Type,
|
||||
markup_node_pool,
|
||||
mark_node_pool,
|
||||
),
|
||||
Expr2::Call { expr: expr_id, .. } => {
|
||||
let expr = env.pool.get(*expr_id);
|
||||
expr2_to_markup(arena, env, expr, *expr_id, markup_node_pool)
|
||||
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,
|
||||
expr2_node_id,
|
||||
HighlightStyle::Variable,
|
||||
markup_node_pool,
|
||||
)
|
||||
new_markup_node(text, ast_node_id, HighlightStyle::Variable, mark_node_pool)
|
||||
}
|
||||
Expr2::List { elems, .. } => {
|
||||
let mut children_ids = vec![new_markup_node(
|
||||
LEFT_SQUARE_BR.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
)];
|
||||
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();
|
||||
|
@ -308,64 +397,43 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
env,
|
||||
sub_expr2,
|
||||
*node_id,
|
||||
markup_node_pool,
|
||||
));
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?);
|
||||
|
||||
if idx + 1 < elems.len() {
|
||||
children_ids.push(new_markup_node(
|
||||
", ".to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||
}
|
||||
}
|
||||
children_ids.push(new_markup_node(
|
||||
RIGHT_SQUARE_BR.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
children_ids.push(mark_node_pool.add(new_right_square_mn(expr2_node_id, None)));
|
||||
|
||||
let list_node = MarkupNode::Nested {
|
||||
ast_node_id: expr2_node_id,
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
markup_node_pool.add(list_node)
|
||||
mark_node_pool.add(list_node)
|
||||
}
|
||||
Expr2::EmptyRecord => {
|
||||
let children_ids = vec![
|
||||
new_markup_node(
|
||||
LEFT_ACCOLADE.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
),
|
||||
new_markup_node(
|
||||
RIGHT_ACCOLADE.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
),
|
||||
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: expr2_node_id,
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
markup_node_pool.add(record_node)
|
||||
mark_node_pool.add(record_node)
|
||||
}
|
||||
Expr2::Record { fields, .. } => {
|
||||
let mut children_ids = vec![new_markup_node(
|
||||
LEFT_ACCOLADE.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
)];
|
||||
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);
|
||||
|
@ -374,21 +442,16 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
|
||||
children_ids.push(new_markup_node(
|
||||
field_name.as_str(env.pool).to_owned(),
|
||||
expr2_node_id,
|
||||
ast_node_id,
|
||||
HighlightStyle::RecordField,
|
||||
markup_node_pool,
|
||||
mark_node_pool,
|
||||
));
|
||||
|
||||
match record_field {
|
||||
RecordField::InvalidLabelOnly(_, _) => (),
|
||||
RecordField::LabelOnly(_, _, _) => (),
|
||||
RecordField::LabeledValue(_, _, sub_expr2_node_id) => {
|
||||
children_ids.push(new_markup_node(
|
||||
COLON.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
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(
|
||||
|
@ -396,66 +459,117 @@ pub fn expr2_to_markup<'a, 'b>(
|
|||
env,
|
||||
sub_expr2,
|
||||
*sub_expr2_node_id,
|
||||
markup_node_pool,
|
||||
));
|
||||
mark_node_pool,
|
||||
interns,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
if idx + 1 < fields.len() {
|
||||
children_ids.push(new_markup_node(
|
||||
", ".to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Operator,
|
||||
markup_node_pool,
|
||||
));
|
||||
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||
}
|
||||
}
|
||||
|
||||
children_ids.push(new_markup_node(
|
||||
RIGHT_ACCOLADE.to_string(),
|
||||
expr2_node_id,
|
||||
HighlightStyle::Bracket,
|
||||
markup_node_pool,
|
||||
));
|
||||
children_ids.push(mark_node_pool.add(new_right_accolade_mn(expr2_node_id, None)));
|
||||
|
||||
let record_node = MarkupNode::Nested {
|
||||
ast_node_id: expr2_node_id,
|
||||
ast_node_id,
|
||||
children_ids,
|
||||
parent_id_opt: None,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
markup_node_pool.add(record_node)
|
||||
mark_node_pool.add(record_node)
|
||||
}
|
||||
Expr2::Blank => markup_node_pool.add(MarkupNode::Blank {
|
||||
ast_node_id: expr2_node_id,
|
||||
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(),
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
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(),
|
||||
expr2_node_id,
|
||||
ast_node_id,
|
||||
HighlightStyle::Blank,
|
||||
markup_node_pool,
|
||||
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, markup_node_pool: &mut SlowPool) {
|
||||
let node = markup_node_pool.get(markup_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, markup_node_pool);
|
||||
set_parent_for_all_helper(child_id, markup_node_id, mark_node_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,9 +577,9 @@ pub fn set_parent_for_all(markup_node_id: MarkNodeId, markup_node_pool: &mut Slo
|
|||
pub fn set_parent_for_all_helper(
|
||||
markup_node_id: MarkNodeId,
|
||||
parent_node_id: MarkNodeId,
|
||||
markup_node_pool: &mut SlowPool,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
) {
|
||||
let node = markup_node_pool.get_mut(markup_node_id);
|
||||
let node = mark_node_pool.get_mut(markup_node_id);
|
||||
|
||||
match node {
|
||||
MarkupNode::Nested {
|
||||
|
@ -479,7 +593,7 @@ pub fn set_parent_for_all_helper(
|
|||
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);
|
||||
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),
|
||||
|
@ -487,23 +601,237 @@ pub fn set_parent_for_all_helper(
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
) -> EdResult<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 {
|
||||
writeln!(
|
||||
write!(
|
||||
f,
|
||||
"{} ({})",
|
||||
"{} ({}, {})",
|
||||
self.node_type_as_string(),
|
||||
self.get_content().unwrap_or_else(|_| "".to_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\n(mark_node_tree)\n".to_owned();
|
||||
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!("{}", node));
|
||||
full_string.push_str(&format!("{} mn_id {}\n", node, root_node_id));
|
||||
|
||||
tree_as_string_helper(node, 1, &mut full_string, mark_node_pool);
|
||||
|
||||
|
@ -524,11 +852,25 @@ fn tree_as_string_helper(
|
|||
.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!("{}", child));
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod code_lines;
|
||||
mod config;
|
||||
mod ed_error;
|
||||
pub mod ed_error;
|
||||
mod grid_node_map;
|
||||
mod keyboard_input;
|
||||
pub mod main;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use super::app_model::AppModel;
|
||||
use super::ed_update;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::window::keyboard_input::Modifiers;
|
||||
use winit::event::VirtualKeyCode;
|
||||
use crate::{editor::ed_error::EdResult, window::keyboard_input::from_winit};
|
||||
use winit::event::{ModifiersState, VirtualKeyCode};
|
||||
|
||||
pub fn handle_copy(app_model: &mut AppModel) -> EdResult<()> {
|
||||
if let Some(ref mut ed_model) = app_model.ed_model_opt {
|
||||
|
@ -51,16 +51,26 @@ pub fn pass_keydown_to_focused(
|
|||
pub enum InputOutcome {
|
||||
Accepted,
|
||||
Ignored,
|
||||
SilentIgnored,
|
||||
}
|
||||
|
||||
pub fn handle_new_char(received_char: &char, app_model: &mut AppModel) -> EdResult<InputOutcome> {
|
||||
pub fn handle_new_char(
|
||||
received_char: &char,
|
||||
app_model: &mut AppModel,
|
||||
modifiers_winit: ModifiersState,
|
||||
) -> EdResult<InputOutcome> {
|
||||
if let Some(ref mut ed_model) = app_model.ed_model_opt {
|
||||
if ed_model.has_focus {
|
||||
let modifiers = from_winit(&modifiers_winit);
|
||||
|
||||
if modifiers.new_char_modifiers() {
|
||||
// shortcuts with modifiers are handled by ed_handle_key_down
|
||||
return ed_update::handle_new_char(received_char, ed_model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(InputOutcome::Ignored)
|
||||
Ok(InputOutcome::SilentIgnored)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
80
editor/src/editor/mvc/break_line.rs
Normal file
80
editor/src/editor/mvc/break_line.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::markup::common_nodes::new_blank_mn_w_nls;
|
||||
use crate::editor::mvc::app_update::InputOutcome;
|
||||
use crate::editor::mvc::ed_model::EdModel;
|
||||
use crate::editor::util::index_of;
|
||||
use crate::lang::ast::Def2;
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
|
||||
// put everything after caret on new line, create a Def2::Blank if there was nothing after the caret.
|
||||
pub fn break_line(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
||||
let carets = ed_model.get_carets();
|
||||
|
||||
for caret_pos in carets.iter() {
|
||||
let caret_line_nr = caret_pos.line;
|
||||
|
||||
// don't allow adding new lines on empty line
|
||||
if caret_pos.column > 0
|
||||
&& ed_model.grid_node_map.node_exists_at_pos(TextPos {
|
||||
line: caret_line_nr,
|
||||
column: caret_pos.column - 1,
|
||||
})
|
||||
{
|
||||
// one blank line between top level definitions
|
||||
EdModel::insert_empty_line(
|
||||
caret_line_nr + 1,
|
||||
&mut ed_model.code_lines,
|
||||
&mut ed_model.grid_node_map,
|
||||
)?;
|
||||
EdModel::insert_empty_line(
|
||||
caret_line_nr + 1,
|
||||
&mut ed_model.code_lines,
|
||||
&mut ed_model.grid_node_map,
|
||||
)?;
|
||||
|
||||
insert_new_blank(ed_model, caret_pos, caret_pos.line + 2)?;
|
||||
}
|
||||
}
|
||||
|
||||
ed_model.simple_move_carets_down(2); // one blank line between top level definitions
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
}
|
||||
|
||||
pub fn insert_new_blank(
|
||||
ed_model: &mut EdModel,
|
||||
caret_pos: &TextPos,
|
||||
insert_on_line_nr: usize,
|
||||
) -> EdResult<()> {
|
||||
let new_line_blank = Def2::Blank;
|
||||
let new_line_blank_id = ed_model.module.env.pool.add(new_line_blank);
|
||||
|
||||
let prev_def_mn_id = ed_model
|
||||
.grid_node_map
|
||||
.get_def_mark_node_id_before_line(caret_pos.line + 1, &ed_model.mark_node_pool)?;
|
||||
let prev_def_mn_id_indx = index_of(prev_def_mn_id, &ed_model.markup_ids)?;
|
||||
ed_model
|
||||
.module
|
||||
.ast
|
||||
.def_ids
|
||||
.insert(prev_def_mn_id_indx, new_line_blank_id);
|
||||
|
||||
let blank_mn_id = ed_model.add_mark_node(new_blank_mn_w_nls(
|
||||
ASTNodeId::ADefId(new_line_blank_id),
|
||||
None,
|
||||
2,
|
||||
));
|
||||
|
||||
ed_model
|
||||
.markup_ids
|
||||
.insert(prev_def_mn_id_indx + 1, blank_mn_id); // + 1 because first markup node is header
|
||||
|
||||
ed_model.insert_all_between_line(
|
||||
insert_on_line_nr, // one blank line between top level definitions
|
||||
0,
|
||||
&[blank_mn_id],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,27 +1,22 @@
|
|||
use crate::editor::code_lines::CodeLines;
|
||||
use crate::editor::grid_node_map::GridNodeMap;
|
||||
use crate::editor::markup::nodes::ast_to_mark_nodes;
|
||||
use crate::editor::slow_pool::{MarkNodeId, SlowPool};
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::editor::{
|
||||
ed_error::EdError::ParseError,
|
||||
ed_error::{EdResult, MissingParent, NoNodeAtCaretPosition},
|
||||
markup::attribute::Attributes,
|
||||
markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode},
|
||||
ed_error::SrcParseError,
|
||||
ed_error::{EdResult, EmptyCodeString, MissingParent, NoNodeAtCaretPosition},
|
||||
};
|
||||
use crate::graphics::primitives::rect::Rect;
|
||||
use crate::lang::ast::{Expr2, ExprId};
|
||||
use crate::lang::expr::{str_to_expr2, Env};
|
||||
use crate::lang::expr::Env;
|
||||
use crate::lang::parse::{ASTNodeId, AST};
|
||||
use crate::lang::pool::PoolStr;
|
||||
use crate::lang::scope::Scope;
|
||||
use crate::ui::text::caret_w_select::CaretWSelect;
|
||||
use crate::ui::text::caret_w_select::{CaretPos, CaretWSelect};
|
||||
use crate::ui::text::lines::SelectableLines;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use nonempty::NonEmpty;
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_region::all::Region;
|
||||
use roc_load::file::LoadedModule;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -31,83 +26,98 @@ pub struct EdModel<'a> {
|
|||
pub code_lines: CodeLines,
|
||||
// allows us to map window coordinates to MarkNodeId's
|
||||
pub grid_node_map: GridNodeMap,
|
||||
pub markup_root_id: MarkNodeId,
|
||||
pub markup_node_pool: SlowPool,
|
||||
pub markup_ids: Vec<MarkNodeId>, // one root node for every expression
|
||||
pub mark_node_pool: SlowPool,
|
||||
// contains single char dimensions, used to calculate line height, column width...
|
||||
pub glyph_dim_rect_opt: Option<Rect>,
|
||||
pub has_focus: bool,
|
||||
pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option<MarkNodeId>)>,
|
||||
pub selected_expr_opt: Option<SelectedExpression>,
|
||||
pub interns: &'a Interns, // this should eventually come from LoadedModule, see #1442
|
||||
pub selected_block_opt: Option<SelectedBlock>,
|
||||
pub loaded_module: LoadedModule,
|
||||
pub show_debug_view: bool,
|
||||
// EdModel is dirty if it has changed since the previous render.
|
||||
pub dirty: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct SelectedExpression {
|
||||
pub ast_node_id: ExprId,
|
||||
pub struct SelectedBlock {
|
||||
pub ast_node_id: ASTNodeId,
|
||||
pub mark_node_id: MarkNodeId,
|
||||
pub type_str: PoolStr,
|
||||
}
|
||||
|
||||
pub fn init_model<'a>(
|
||||
code_str: &'a BumpString,
|
||||
code_str: &'a str,
|
||||
file_path: &'a Path,
|
||||
env: Env<'a>,
|
||||
interns: &'a Interns,
|
||||
loaded_module: LoadedModule,
|
||||
code_arena: &'a Bump,
|
||||
caret_pos: CaretPos, // to set caret position
|
||||
) -> EdResult<EdModel<'a>> {
|
||||
let mut module = EdModule::new(code_str, env, code_arena)?;
|
||||
|
||||
let ast_root_id = module.ast_root_id;
|
||||
let mut markup_node_pool = SlowPool::new();
|
||||
let mut mark_node_pool = SlowPool::new();
|
||||
|
||||
let markup_root_id = if code_str.is_empty() {
|
||||
let blank_root = MarkupNode::Blank {
|
||||
ast_node_id: ast_root_id,
|
||||
attributes: Attributes::new(),
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
parent_id_opt: None,
|
||||
};
|
||||
|
||||
markup_node_pool.add(blank_root)
|
||||
let markup_ids = if code_str.is_empty() {
|
||||
EmptyCodeString {}.fail()
|
||||
} else {
|
||||
let ast_root = &module.env.pool.get(ast_root_id);
|
||||
|
||||
let temp_markup_root_id = expr2_to_markup(
|
||||
ast_to_mark_nodes(
|
||||
code_arena,
|
||||
&mut module.env,
|
||||
ast_root,
|
||||
ast_root_id,
|
||||
&mut markup_node_pool,
|
||||
);
|
||||
set_parent_for_all(temp_markup_root_id, &mut markup_node_pool);
|
||||
&module.ast,
|
||||
&mut mark_node_pool,
|
||||
&loaded_module.interns,
|
||||
)
|
||||
}?;
|
||||
|
||||
temp_markup_root_id
|
||||
let mut code_lines = CodeLines::default();
|
||||
let mut grid_node_map = GridNodeMap::default();
|
||||
|
||||
let mut line_nr = 0;
|
||||
let mut col_nr = 0;
|
||||
|
||||
for mark_node_id in &markup_ids {
|
||||
EdModel::insert_mark_node_between_line(
|
||||
&mut line_nr,
|
||||
&mut col_nr,
|
||||
*mark_node_id,
|
||||
&mut grid_node_map,
|
||||
&mut code_lines,
|
||||
&mark_node_pool,
|
||||
)?
|
||||
}
|
||||
|
||||
let caret = match caret_pos {
|
||||
CaretPos::Start => CaretWSelect::default(),
|
||||
CaretPos::Exact(txt_pos) => CaretWSelect::new(txt_pos, None),
|
||||
CaretPos::End => CaretWSelect::new(code_lines.end_txt_pos(), None),
|
||||
};
|
||||
|
||||
let code_lines = EdModel::build_code_lines_from_markup(markup_root_id, &markup_node_pool)?;
|
||||
let grid_node_map = EdModel::build_node_map_from_markup(markup_root_id, &markup_node_pool)?;
|
||||
|
||||
Ok(EdModel {
|
||||
module,
|
||||
file_path,
|
||||
code_lines,
|
||||
grid_node_map,
|
||||
markup_root_id,
|
||||
markup_node_pool,
|
||||
markup_ids,
|
||||
mark_node_pool,
|
||||
glyph_dim_rect_opt: None,
|
||||
has_focus: true,
|
||||
caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)),
|
||||
selected_expr_opt: None,
|
||||
interns,
|
||||
caret_w_select_vec: NonEmpty::new((caret, None)),
|
||||
selected_block_opt: None,
|
||||
loaded_module,
|
||||
show_debug_view: false,
|
||||
dirty: true,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> EdModel<'a> {
|
||||
pub fn get_carets(&self) -> Vec<TextPos> {
|
||||
self.caret_w_select_vec
|
||||
.iter()
|
||||
.map(|tup| tup.0.caret_pos)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_curr_mark_node_id(&self) -> UIResult<MarkNodeId> {
|
||||
let caret_pos = self.get_caret();
|
||||
self.grid_node_map.get_id_at_row_col(caret_pos)
|
||||
|
@ -138,11 +148,11 @@ impl<'a> EdModel<'a> {
|
|||
pub fn get_curr_child_indices(&self) -> EdResult<(usize, usize)> {
|
||||
if self.node_exists_at_caret() {
|
||||
let curr_mark_node_id = self.get_curr_mark_node_id()?;
|
||||
let curr_mark_node = self.markup_node_pool.get(curr_mark_node_id);
|
||||
let curr_mark_node = self.mark_node_pool.get(curr_mark_node_id);
|
||||
|
||||
if let Some(parent_id) = curr_mark_node.get_parent_id_opt() {
|
||||
let parent = self.markup_node_pool.get(parent_id);
|
||||
parent.get_child_indices(curr_mark_node_id, &self.markup_node_pool)
|
||||
let parent = self.mark_node_pool.get(parent_id);
|
||||
parent.get_child_indices(curr_mark_node_id, &self.mark_node_pool)
|
||||
} else {
|
||||
MissingParent {
|
||||
node_id: curr_mark_node_id,
|
||||
|
@ -161,38 +171,26 @@ impl<'a> EdModel<'a> {
|
|||
#[derive(Debug)]
|
||||
pub struct EdModule<'a> {
|
||||
pub env: Env<'a>,
|
||||
pub ast_root_id: ExprId,
|
||||
pub ast: AST,
|
||||
}
|
||||
|
||||
// for debugging
|
||||
// use crate::lang::ast::expr2_to_string;
|
||||
//use crate::lang::ast::expr2_to_string;
|
||||
|
||||
impl<'a> EdModule<'a> {
|
||||
pub fn new(code_str: &'a str, mut env: Env<'a>, ast_arena: &'a Bump) -> EdResult<EdModule<'a>> {
|
||||
if !code_str.is_empty() {
|
||||
let mut scope = Scope::new(env.home, env.pool, env.var_store);
|
||||
let parse_res = AST::parse_from_string(code_str, &mut env, ast_arena);
|
||||
|
||||
let region = Region::new(0, 0, 0, 0);
|
||||
|
||||
let expr2_result = str_to_expr2(ast_arena, code_str, &mut env, &mut scope, region);
|
||||
|
||||
match expr2_result {
|
||||
Ok((expr2, _output)) => {
|
||||
let ast_root_id = env.pool.add(expr2);
|
||||
|
||||
// for debugging
|
||||
// dbg!(expr2_to_string(ast_root_id, env.pool));
|
||||
|
||||
Ok(EdModule { env, ast_root_id })
|
||||
}
|
||||
Err(err) => Err(ParseError {
|
||||
match parse_res {
|
||||
Ok(ast) => Ok(EdModule { env, ast }),
|
||||
Err(err) => SrcParseError {
|
||||
syntax_err: format!("{:?}", err),
|
||||
}),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
} else {
|
||||
let ast_root_id = env.pool.add(Expr2::Blank);
|
||||
|
||||
Ok(EdModule { env, ast_root_id })
|
||||
EmptyCodeString {}.fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,40 +198,49 @@ impl<'a> EdModule<'a> {
|
|||
#[cfg(test)]
|
||||
pub mod test_ed_model {
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::main::load_module;
|
||||
use crate::editor::mvc::ed_model;
|
||||
use crate::editor::resources::strings::HELLO_WORLD;
|
||||
use crate::lang::expr::Env;
|
||||
use crate::lang::pool::Pool;
|
||||
use crate::ui::text::caret_w_select::test_caret_w_select::convert_dsl_to_selection;
|
||||
use crate::ui::text::caret_w_select::test_caret_w_select::convert_selection_to_dsl;
|
||||
use crate::ui::text::caret_w_select::CaretPos;
|
||||
use crate::ui::text::lines::SelectableLines;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
use crate::ui::ui_error::UIResult;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use ed_model::EdModel;
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleIds};
|
||||
use roc_load::file::LoadedModule;
|
||||
use roc_module::symbol::IdentIds;
|
||||
use roc_module::symbol::ModuleIds;
|
||||
use roc_types::subs::VarStore;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn init_dummy_model<'a>(
|
||||
code_str: &'a BumpString,
|
||||
code_str: &'a str,
|
||||
loaded_module: LoadedModule,
|
||||
module_ids: &'a ModuleIds,
|
||||
ed_model_refs: &'a mut EdModelRefs,
|
||||
code_arena: &'a Bump,
|
||||
) -> EdResult<EdModel<'a>> {
|
||||
let file_path = Path::new("");
|
||||
|
||||
let dep_idents = IdentIds::exposed_builtins(8);
|
||||
let exposed_ident_ids = IdentIds::default();
|
||||
let mod_id = ed_model_refs
|
||||
.interns
|
||||
.module_ids
|
||||
.get_or_insert(&"ModId123".into());
|
||||
|
||||
let env = Env::new(
|
||||
mod_id,
|
||||
loaded_module.module_id,
|
||||
&ed_model_refs.env_arena,
|
||||
&mut ed_model_refs.env_pool,
|
||||
&mut ed_model_refs.var_store,
|
||||
dep_idents,
|
||||
&ed_model_refs.interns.module_ids,
|
||||
module_ids,
|
||||
exposed_ident_ids,
|
||||
);
|
||||
|
||||
|
@ -241,43 +248,69 @@ pub mod test_ed_model {
|
|||
code_str,
|
||||
file_path,
|
||||
env,
|
||||
&ed_model_refs.interns,
|
||||
&ed_model_refs.code_arena,
|
||||
loaded_module,
|
||||
code_arena,
|
||||
CaretPos::End,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct EdModelRefs {
|
||||
code_arena: Bump,
|
||||
env_arena: Bump,
|
||||
env_pool: Pool,
|
||||
var_store: VarStore,
|
||||
interns: Interns,
|
||||
}
|
||||
|
||||
pub fn init_model_refs() -> EdModelRefs {
|
||||
EdModelRefs {
|
||||
code_arena: Bump::new(),
|
||||
env_arena: Bump::new(),
|
||||
env_pool: Pool::with_capacity(1024),
|
||||
var_store: VarStore::default(),
|
||||
interns: Interns {
|
||||
module_ids: ModuleIds::default(),
|
||||
all_ident_ids: IdentIds::exposed_builtins(8),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ed_model_from_dsl<'a>(
|
||||
clean_code_str: &'a BumpString,
|
||||
code_lines: &[&str],
|
||||
clean_code_str: &'a mut String,
|
||||
code_lines: Vec<String>,
|
||||
ed_model_refs: &'a mut EdModelRefs,
|
||||
module_ids: &'a ModuleIds,
|
||||
code_arena: &'a Bump,
|
||||
) -> Result<EdModel<'a>, String> {
|
||||
let code_lines_vec: Vec<String> = (*code_lines).iter().map(|s| s.to_string()).collect();
|
||||
let caret_w_select = convert_dsl_to_selection(&code_lines_vec)?;
|
||||
let full_code = vec![HELLO_WORLD, clean_code_str.as_str()];
|
||||
*clean_code_str = full_code.join("\n");
|
||||
|
||||
let mut ed_model = init_dummy_model(clean_code_str, ed_model_refs)?;
|
||||
let temp_dir = tempdir().expect("Failed to create temporary directory for test.");
|
||||
let temp_file_path_buf =
|
||||
PathBuf::from([Uuid::new_v4().to_string(), ".roc".to_string()].join(""));
|
||||
let temp_file_full_path = temp_dir.path().join(temp_file_path_buf);
|
||||
|
||||
ed_model.set_caret(caret_w_select.caret_pos);
|
||||
let mut file = File::create(temp_file_full_path.clone()).expect(&format!(
|
||||
"Failed to create temporary file for path {:?}",
|
||||
temp_file_full_path
|
||||
));
|
||||
writeln!(file, "{}", clean_code_str).expect(&format!(
|
||||
"Failed to write {:?} to file: {:?}",
|
||||
clean_code_str, file
|
||||
));
|
||||
|
||||
let loaded_module = load_module(&temp_file_full_path);
|
||||
|
||||
let mut ed_model = init_dummy_model(
|
||||
clean_code_str,
|
||||
loaded_module,
|
||||
module_ids,
|
||||
ed_model_refs,
|
||||
code_arena,
|
||||
)?;
|
||||
|
||||
// adjust for header and main function
|
||||
let nr_hello_world_lines = HELLO_WORLD.matches('\n').count() - 2;
|
||||
let caret_w_select = convert_dsl_to_selection(&code_lines)?;
|
||||
let adjusted_caret_pos = TextPos {
|
||||
line: caret_w_select.caret_pos.line + nr_hello_world_lines,
|
||||
column: caret_w_select.caret_pos.column,
|
||||
};
|
||||
|
||||
ed_model.set_caret(adjusted_caret_pos);
|
||||
|
||||
Ok(ed_model)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
use super::ed_model::EdModel;
|
||||
use crate::editor::config::Config;
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::mvc::ed_model::SelectedExpression;
|
||||
use crate::editor::mvc::ed_model::SelectedBlock;
|
||||
use crate::editor::render_ast::build_code_graphics;
|
||||
use crate::editor::render_debug::build_debug_graphics;
|
||||
use crate::editor::resources::strings::START_TIP;
|
||||
|
@ -20,33 +20,49 @@ use winit::dpi::PhysicalSize;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderedWgpu {
|
||||
pub text_sections: Vec<glyph_brush::OwnedSection>,
|
||||
pub rects: Vec<Rect>,
|
||||
pub text_sections_behind: Vec<glyph_brush::OwnedSection>, // displayed in front of rect_behind, behind everything else
|
||||
pub text_sections_front: Vec<glyph_brush::OwnedSection>, // displayed in front of everything
|
||||
pub rects_behind: Vec<Rect>, // displayed at lowest depth
|
||||
pub rects_front: Vec<Rect>, // displayed in front of text_sections_behind, behind text_sections_front
|
||||
}
|
||||
|
||||
impl RenderedWgpu {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
text_sections: Vec::new(),
|
||||
rects: Vec::new(),
|
||||
text_sections_behind: Vec::new(),
|
||||
text_sections_front: Vec::new(),
|
||||
rects_behind: Vec::new(),
|
||||
rects_front: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_text(&mut self, new_text_section: glyph_brush::OwnedSection) {
|
||||
self.text_sections.push(new_text_section);
|
||||
pub fn add_text_behind(&mut self, new_text_section: glyph_brush::OwnedSection) {
|
||||
self.text_sections_behind.push(new_text_section);
|
||||
}
|
||||
|
||||
pub fn add_rect(&mut self, new_rect: Rect) {
|
||||
self.rects.push(new_rect);
|
||||
pub fn add_text_front(&mut self, new_text_section: glyph_brush::OwnedSection) {
|
||||
self.text_sections_front.push(new_text_section);
|
||||
}
|
||||
|
||||
pub fn add_rects(&mut self, new_rects: Vec<Rect>) {
|
||||
self.rects.extend(new_rects);
|
||||
pub fn add_rect_behind(&mut self, new_rect: Rect) {
|
||||
self.rects_behind.push(new_rect);
|
||||
}
|
||||
|
||||
pub fn add_rects_behind(&mut self, new_rects: Vec<Rect>) {
|
||||
self.rects_behind.extend(new_rects);
|
||||
}
|
||||
|
||||
pub fn add_rect_front(&mut self, new_rect: Rect) {
|
||||
self.rects_front.push(new_rect);
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, rendered_wgpu: RenderedWgpu) {
|
||||
self.text_sections.extend(rendered_wgpu.text_sections);
|
||||
self.rects.extend(rendered_wgpu.rects);
|
||||
self.text_sections_behind
|
||||
.extend(rendered_wgpu.text_sections_behind);
|
||||
self.text_sections_front
|
||||
.extend(rendered_wgpu.text_sections_front);
|
||||
self.rects_behind.extend(rendered_wgpu.rects_behind);
|
||||
self.rects_front.extend(rendered_wgpu.rects_front);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +77,10 @@ pub fn model_to_wgpu<'a>(
|
|||
|
||||
let mut all_rendered = RenderedWgpu::new();
|
||||
|
||||
let tip_txt_coords = (txt_coords.x, txt_coords.y - 4.0 * config.code_font_size);
|
||||
let tip_txt_coords = (
|
||||
txt_coords.x,
|
||||
txt_coords.y - (START_TIP.matches('\n').count() as f32 + 1.0) * config.code_font_size,
|
||||
);
|
||||
|
||||
let start_tip_text = owned_section_from_text(&Text {
|
||||
position: tip_txt_coords.into(),
|
||||
|
@ -72,15 +91,15 @@ pub fn model_to_wgpu<'a>(
|
|||
..Default::default()
|
||||
});
|
||||
|
||||
all_rendered.add_text(start_tip_text);
|
||||
all_rendered.add_text_behind(start_tip_text);
|
||||
|
||||
let rendered_code_graphics = build_code_graphics(
|
||||
ed_model.markup_node_pool.get(ed_model.markup_root_id),
|
||||
&ed_model.markup_ids,
|
||||
size,
|
||||
txt_coords,
|
||||
config,
|
||||
glyph_dim_rect,
|
||||
&ed_model.markup_node_pool,
|
||||
&ed_model.mark_node_pool,
|
||||
)?;
|
||||
|
||||
all_rendered.extend(rendered_code_graphics);
|
||||
|
@ -93,7 +112,7 @@ pub fn model_to_wgpu<'a>(
|
|||
|
||||
let rendered_selection = build_selection_graphics(
|
||||
caret_w_sel_vec,
|
||||
&ed_model.selected_expr_opt,
|
||||
&ed_model.selected_block_opt,
|
||||
txt_coords,
|
||||
config,
|
||||
glyph_dim_rect,
|
||||
|
@ -103,7 +122,7 @@ pub fn model_to_wgpu<'a>(
|
|||
all_rendered.extend(rendered_selection);
|
||||
|
||||
if ed_model.show_debug_view {
|
||||
all_rendered.add_text(build_debug_graphics(size, txt_coords, config, ed_model)?);
|
||||
all_rendered.add_text_behind(build_debug_graphics(size, txt_coords, config, ed_model)?);
|
||||
}
|
||||
|
||||
Ok(all_rendered)
|
||||
|
@ -111,7 +130,7 @@ pub fn model_to_wgpu<'a>(
|
|||
|
||||
pub fn build_selection_graphics(
|
||||
caret_w_select_vec: Vec<CaretWSelect>,
|
||||
selected_expr_opt: &Option<SelectedExpression>,
|
||||
selected_expr_opt: &Option<SelectedBlock>,
|
||||
txt_coords: Vector2<f32>,
|
||||
config: &Config,
|
||||
glyph_dim_rect: Rect,
|
||||
|
@ -139,7 +158,7 @@ pub fn build_selection_graphics(
|
|||
let width =
|
||||
((end_pos.column as f32) * char_width) - ((start_pos.column as f32) * char_width);
|
||||
|
||||
all_rendered.add_rect(make_selection_rect(
|
||||
all_rendered.add_rect_behind(make_selection_rect(
|
||||
sel_rect_x,
|
||||
sel_rect_y,
|
||||
width,
|
||||
|
@ -158,12 +177,12 @@ pub fn build_selection_graphics(
|
|||
let (tip_rect, tip_text) =
|
||||
tooltip.render_tooltip(&glyph_dim_rect, &config.ed_theme.ui_theme);
|
||||
|
||||
all_rendered.add_rect(tip_rect);
|
||||
all_rendered.add_text(tip_text);
|
||||
all_rendered.add_rect_front(tip_rect);
|
||||
all_rendered.add_text_front(tip_text);
|
||||
}
|
||||
}
|
||||
|
||||
all_rendered.add_rect(make_caret_rect(
|
||||
all_rendered.add_rect_front(make_caret_rect(
|
||||
top_left_x,
|
||||
top_left_y,
|
||||
&glyph_dim_rect,
|
||||
|
|
|
@ -25,6 +25,7 @@ pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<Inpu
|
|||
} = get_node_context(ed_model)?;
|
||||
|
||||
let is_blank_node = curr_mark_node.is_blank();
|
||||
let curr_mark_node_nls = curr_mark_node.get_newlines_at_end();
|
||||
|
||||
let int_var = ed_model.module.env.var_store.fresh();
|
||||
|
||||
|
@ -37,7 +38,11 @@ pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<Inpu
|
|||
text: PoolStr::new(&digit_string, &mut ed_model.module.env.pool),
|
||||
};
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, expr2_node);
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, expr2_node);
|
||||
|
||||
let int_node = MarkupNode::Text {
|
||||
content: digit_string,
|
||||
|
@ -45,11 +50,12 @@ pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<Inpu
|
|||
syn_high_style: HighlightStyle::Number,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: curr_mark_node_nls,
|
||||
};
|
||||
|
||||
if is_blank_node {
|
||||
ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, int_node);
|
||||
|
||||
// remove data corresponding to Blank node
|
||||
|
@ -59,11 +65,13 @@ pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<Inpu
|
|||
ed_model.simple_move_carets_right(char_len);
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
&digit_char.to_string(),
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
@ -85,7 +93,7 @@ pub fn update_int(
|
|||
.grid_node_map
|
||||
.get_offset_to_node_id(old_caret_pos, int_mark_node_id)?;
|
||||
|
||||
let int_mark_node = ed_model.markup_node_pool.get_mut(int_mark_node_id);
|
||||
let int_mark_node = ed_model.mark_node_pool.get_mut(int_mark_node_id);
|
||||
let int_ast_node_id = int_mark_node.get_ast_node_id();
|
||||
|
||||
let content_str_mut = int_mark_node.get_content_mut()?;
|
||||
|
@ -98,19 +106,25 @@ pub fn update_int(
|
|||
} else {
|
||||
content_str_mut.insert(node_caret_offset, *ch);
|
||||
|
||||
let content_str = int_mark_node.get_content()?;
|
||||
let content_str = int_mark_node.get_content();
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
&ch.to_string(),
|
||||
int_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
// update ast
|
||||
let new_pool_str = PoolStr::new(&content_str, ed_model.module.env.pool);
|
||||
let int_ast_node = ed_model.module.env.pool.get_mut(int_ast_node_id);
|
||||
let int_ast_node = ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.get_mut(int_ast_node_id.to_expr_id()?);
|
||||
match int_ast_node {
|
||||
SmallInt { number, text, .. } => {
|
||||
update_small_int_num(number, &content_str)?;
|
||||
|
|
185
editor/src/editor/mvc/let_update.rs
Normal file
185
editor/src/editor/mvc/let_update.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::markup::attribute::Attributes;
|
||||
use crate::editor::markup::common_nodes::new_blank_mn_w_nls;
|
||||
use crate::editor::markup::common_nodes::new_equals_mn;
|
||||
use crate::editor::markup::nodes::MarkupNode;
|
||||
use crate::editor::mvc::app_update::InputOutcome;
|
||||
use crate::editor::mvc::ed_model::EdModel;
|
||||
use crate::editor::mvc::ed_update::get_node_context;
|
||||
use crate::editor::mvc::ed_update::NodeContext;
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::lang::ast::{Expr2, ValueDef};
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::lang::pattern::Pattern2;
|
||||
|
||||
pub fn start_new_let_value(ed_model: &mut EdModel, new_char: &char) -> EdResult<InputOutcome> {
|
||||
let NodeContext {
|
||||
old_caret_pos,
|
||||
curr_mark_node_id,
|
||||
curr_mark_node,
|
||||
parent_id_opt,
|
||||
ast_node_id,
|
||||
} = get_node_context(ed_model)?;
|
||||
|
||||
let is_blank_node = curr_mark_node.is_blank();
|
||||
let curr_mark_node_nls = curr_mark_node.get_newlines_at_end();
|
||||
|
||||
let val_name_string = new_char.to_string();
|
||||
// safe unwrap because our ArrString has a 30B capacity
|
||||
let val_expr2_node = Expr2::Blank;
|
||||
let val_expr_id = ed_model.module.env.pool.add(val_expr2_node);
|
||||
|
||||
let ident_id = ed_model
|
||||
.module
|
||||
.env
|
||||
.ident_ids
|
||||
.add(val_name_string.clone().into());
|
||||
let var_symbol = Symbol::new(ed_model.module.env.home, ident_id);
|
||||
let body = Expr2::Var(var_symbol);
|
||||
let body_id = ed_model.module.env.pool.add(body);
|
||||
|
||||
let pattern = Pattern2::Identifier(var_symbol);
|
||||
let pattern_id = ed_model.module.env.pool.add(pattern);
|
||||
|
||||
let value_def = ValueDef::NoAnnotation {
|
||||
pattern_id,
|
||||
expr_id: val_expr_id,
|
||||
expr_var: ed_model.module.env.var_store.fresh(),
|
||||
};
|
||||
let def_id = ed_model.module.env.pool.add(value_def);
|
||||
|
||||
let expr2_node = Expr2::LetValue {
|
||||
def_id,
|
||||
body_id,
|
||||
body_var: ed_model.module.env.var_store.fresh(),
|
||||
};
|
||||
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, expr2_node);
|
||||
|
||||
let val_name_mark_node = MarkupNode::Text {
|
||||
content: val_name_string,
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Variable,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(curr_mark_node_id),
|
||||
newlines_at_end: curr_mark_node_nls,
|
||||
};
|
||||
|
||||
let val_name_mn_id = ed_model.add_mark_node(val_name_mark_node);
|
||||
|
||||
let equals_mn_id = ed_model.add_mark_node(new_equals_mn(ast_node_id, Some(curr_mark_node_id)));
|
||||
|
||||
let body_mn_id = ed_model.add_mark_node(new_blank_mn_w_nls(
|
||||
ASTNodeId::AExprId(val_expr_id),
|
||||
Some(curr_mark_node_id),
|
||||
1,
|
||||
));
|
||||
|
||||
let val_mark_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![val_name_mn_id, equals_mn_id, body_mn_id],
|
||||
parent_id_opt,
|
||||
newlines_at_end: 1,
|
||||
};
|
||||
|
||||
if is_blank_node {
|
||||
ed_model
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, val_mark_node);
|
||||
|
||||
// remove data corresponding to Blank node
|
||||
ed_model.del_blank_expr_node(old_caret_pos)?;
|
||||
|
||||
let char_len = 1;
|
||||
ed_model.simple_move_carets_right(char_len);
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_all_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
&[val_name_mn_id, equals_mn_id, body_mn_id],
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO reenable this for updating non-top level value defs
|
||||
/*
|
||||
pub fn update_let_value(
|
||||
val_name_mn_id: MarkNodeId,
|
||||
def_id: NodeId<ValueDef>,
|
||||
body_id: NodeId<Expr2>,
|
||||
ed_model: &mut EdModel,
|
||||
new_char: &char,
|
||||
) -> EdResult<InputOutcome> {
|
||||
if new_char.is_ascii_alphanumeric() {
|
||||
let old_caret_pos = ed_model.get_caret();
|
||||
|
||||
// update markup
|
||||
let val_name_mn_mut = ed_model.mark_node_pool.get_mut(val_name_mn_id);
|
||||
let content_str_mut = val_name_mn_mut.get_content_mut()?;
|
||||
|
||||
let old_val_name = content_str_mut.clone();
|
||||
|
||||
let node_caret_offset = ed_model
|
||||
.grid_node_map
|
||||
.get_offset_to_node_id(old_caret_pos, val_name_mn_id)?;
|
||||
|
||||
if node_caret_offset <= content_str_mut.len() {
|
||||
content_str_mut.insert(node_caret_offset, *new_char);
|
||||
|
||||
// update ast
|
||||
let value_def = ed_model.module.env.pool.get(def_id);
|
||||
let value_ident_pattern_id = value_def.get_pattern_id();
|
||||
|
||||
// TODO no unwrap
|
||||
let ident_id = ed_model
|
||||
.module
|
||||
.env
|
||||
.ident_ids
|
||||
.update_key(&old_val_name, content_str_mut)
|
||||
.unwrap();
|
||||
|
||||
let new_var_symbol = Symbol::new(ed_model.module.env.home, ident_id);
|
||||
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(value_ident_pattern_id, Pattern2::Identifier(new_var_symbol));
|
||||
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(body_id, Expr2::Var(new_var_symbol));
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
&new_char.to_string(),
|
||||
val_name_mn_id,
|
||||
)?;
|
||||
|
||||
// update caret
|
||||
ed_model.simple_move_carets_right(1);
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,6 +1,8 @@
|
|||
use crate::editor::ed_error::EdResult;
|
||||
use crate::editor::ed_error::{MissingParent, UnexpectedASTNode};
|
||||
use crate::editor::markup::attribute::Attributes;
|
||||
use crate::editor::markup::common_nodes::{
|
||||
new_blank_mn, new_comma_mn, new_left_square_mn, new_right_square_mn,
|
||||
};
|
||||
use crate::editor::markup::nodes;
|
||||
use crate::editor::markup::nodes::MarkupNode;
|
||||
use crate::editor::mvc::app_update::InputOutcome;
|
||||
|
@ -8,9 +10,9 @@ use crate::editor::mvc::ed_model::EdModel;
|
|||
use crate::editor::mvc::ed_update::get_node_context;
|
||||
use crate::editor::mvc::ed_update::NodeContext;
|
||||
use crate::editor::slow_pool::MarkNodeId;
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::lang::ast::Expr2;
|
||||
use crate::lang::ast::{expr2_to_string, ExprId};
|
||||
use crate::lang::ast::ExprId;
|
||||
use crate::lang::ast::{ast_node_to_string, Expr2};
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::lang::pool::PoolVec;
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
|
||||
|
@ -24,63 +26,62 @@ pub fn start_new_list(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
|||
} = get_node_context(ed_model)?;
|
||||
|
||||
let is_blank_node = curr_mark_node.is_blank();
|
||||
let curr_mark_node_nls = curr_mark_node.get_newlines_at_end();
|
||||
|
||||
let expr2_node = Expr2::List {
|
||||
elem_var: ed_model.module.env.var_store.fresh(),
|
||||
elems: PoolVec::empty(ed_model.module.env.pool),
|
||||
};
|
||||
|
||||
let mark_node_pool = &mut ed_model.markup_node_pool;
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, expr2_node);
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, expr2_node);
|
||||
let left_bracket_node_id = ed_model.add_mark_node(new_left_square_mn(
|
||||
ast_node_id.to_expr_id()?,
|
||||
Some(curr_mark_node_id),
|
||||
));
|
||||
|
||||
let left_bracket_node = MarkupNode::Text {
|
||||
content: nodes::LEFT_SQUARE_BR.to_owned(),
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one
|
||||
};
|
||||
|
||||
let left_bracket_node_id = mark_node_pool.add(left_bracket_node);
|
||||
|
||||
let right_bracket_node = MarkupNode::Text {
|
||||
content: nodes::RIGHT_SQUARE_BR.to_owned(),
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one
|
||||
};
|
||||
|
||||
let right_bracket_node_id = mark_node_pool.add(right_bracket_node);
|
||||
let right_bracket_node_id = ed_model.add_mark_node(new_right_square_mn(
|
||||
ast_node_id.to_expr_id()?,
|
||||
Some(curr_mark_node_id),
|
||||
));
|
||||
|
||||
let nested_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
||||
parent_id_opt,
|
||||
newlines_at_end: curr_mark_node_nls,
|
||||
};
|
||||
|
||||
if is_blank_node {
|
||||
mark_node_pool.replace_node(curr_mark_node_id, nested_node);
|
||||
ed_model
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, nested_node);
|
||||
|
||||
// remove data corresponding to Blank node
|
||||
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
|
||||
ed_model.del_blank_expr_node(old_caret_pos)?;
|
||||
|
||||
ed_model.simple_move_carets_right(nodes::LEFT_SQUARE_BR.len());
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
nodes::LEFT_SQUARE_BR,
|
||||
left_bracket_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column + nodes::LEFT_SQUARE_BR.len(),
|
||||
nodes::RIGHT_SQUARE_BR,
|
||||
right_bracket_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
@ -105,10 +106,10 @@ pub fn add_blank_child(
|
|||
|
||||
let trip_result: EdResult<(ExprId, ExprId, MarkNodeId)> = if let Some(parent_id) = parent_id_opt
|
||||
{
|
||||
let parent = ed_model.markup_node_pool.get(parent_id);
|
||||
let parent = ed_model.mark_node_pool.get(parent_id);
|
||||
|
||||
let list_ast_node_id = parent.get_ast_node_id();
|
||||
let list_ast_node = ed_model.module.env.pool.get(list_ast_node_id);
|
||||
let list_ast_node = ed_model.module.env.pool.get(list_ast_node_id.to_expr_id()?);
|
||||
|
||||
match list_ast_node {
|
||||
Expr2::List {
|
||||
|
@ -118,11 +119,11 @@ pub fn add_blank_child(
|
|||
let blank_elt = Expr2::Blank;
|
||||
let blank_elt_id = ed_model.module.env.pool.add(blank_elt);
|
||||
|
||||
Ok((blank_elt_id, list_ast_node_id, parent_id))
|
||||
Ok((blank_elt_id, list_ast_node_id.to_expr_id()?, parent_id))
|
||||
}
|
||||
_ => UnexpectedASTNode {
|
||||
required_node_type: "List".to_string(),
|
||||
encountered_node_type: expr2_to_string(ast_node_id, ed_model.module.env.pool),
|
||||
encountered_node_type: ast_node_to_string(ast_node_id, ed_model.module.env.pool),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ pub fn add_blank_child(
|
|||
}
|
||||
_ => UnexpectedASTNode {
|
||||
required_node_type: "List".to_string(),
|
||||
encountered_node_type: expr2_to_string(ast_node_id, ed_model.module.env.pool),
|
||||
encountered_node_type: ast_node_to_string(ast_node_id, ed_model.module.env.pool),
|
||||
}
|
||||
.fail(),
|
||||
}?;
|
||||
|
@ -173,7 +174,7 @@ pub fn add_blank_child(
|
|||
ed_model,
|
||||
)?;
|
||||
|
||||
let parent = ed_model.markup_node_pool.get_mut(parent_id);
|
||||
let parent = ed_model.mark_node_pool.get_mut(parent_id);
|
||||
|
||||
for (indx, child) in new_mark_children.iter().enumerate() {
|
||||
parent.add_child_at_index(new_child_index + indx, *child)?;
|
||||
|
@ -191,35 +192,26 @@ pub fn update_mark_children(
|
|||
parent_id_opt: Option<MarkNodeId>,
|
||||
ed_model: &mut EdModel,
|
||||
) -> EdResult<Vec<MarkNodeId>> {
|
||||
let blank_mark_node = MarkupNode::Blank {
|
||||
ast_node_id: blank_elt_id,
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
let blank_mark_node_id = ed_model.add_mark_node(new_blank_mn(
|
||||
ASTNodeId::AExprId(blank_elt_id),
|
||||
parent_id_opt,
|
||||
};
|
||||
|
||||
let blank_mark_node_id = ed_model.markup_node_pool.add(blank_mark_node);
|
||||
));
|
||||
|
||||
let mut children: Vec<MarkNodeId> = vec![];
|
||||
|
||||
if new_child_index > 1 {
|
||||
let comma_mark_node = MarkupNode::Text {
|
||||
content: nodes::COMMA.to_owned(),
|
||||
ast_node_id: list_ast_node_id,
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
};
|
||||
|
||||
let comma_mark_node_id = ed_model.markup_node_pool.add(comma_mark_node);
|
||||
let comma_mark_node_id =
|
||||
ed_model.add_mark_node(new_comma_mn(list_ast_node_id, parent_id_opt));
|
||||
|
||||
ed_model.simple_move_carets_right(nodes::COMMA.len());
|
||||
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
nodes::COMMA,
|
||||
comma_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
children.push(comma_mark_node_id);
|
||||
|
@ -234,11 +226,13 @@ pub fn update_mark_children(
|
|||
};
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column + comma_shift,
|
||||
nodes::BLANK_PLACEHOLDER,
|
||||
blank_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
Ok(children)
|
||||
|
|
|
@ -10,7 +10,7 @@ pub fn update_invalid_lookup(
|
|||
input_str: &str,
|
||||
old_pool_str: &PoolStr,
|
||||
curr_mark_node_id: MarkNodeId,
|
||||
ast_node_id: ExprId,
|
||||
expr_id: ExprId,
|
||||
ed_model: &mut EdModel,
|
||||
) -> EdResult<InputOutcome> {
|
||||
if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
||||
|
@ -32,10 +32,10 @@ pub fn update_invalid_lookup(
|
|||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id, Expr2::InvalidLookup(new_pool_str));
|
||||
.set(expr_id, Expr2::InvalidLookup(new_pool_str));
|
||||
|
||||
// update MarkupNode
|
||||
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
|
||||
let curr_mark_node_mut = ed_model.mark_node_pool.get_mut(curr_mark_node_id);
|
||||
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||
content_str_mut.insert_str(caret_offset, input_str);
|
||||
|
||||
|
@ -43,11 +43,13 @@ pub fn update_invalid_lookup(
|
|||
ed_model.simple_move_carets_right(input_str.len());
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
input_str,
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
pub mod app_model;
|
||||
pub mod app_update;
|
||||
mod break_line;
|
||||
pub mod ed_model;
|
||||
pub mod ed_update;
|
||||
pub mod ed_view;
|
||||
mod int_update;
|
||||
mod let_update;
|
||||
mod list_update;
|
||||
mod lookup_update;
|
||||
mod record_update;
|
||||
mod string_update;
|
||||
pub mod tld_value_update;
|
||||
|
|
|
@ -2,6 +2,9 @@ use crate::editor::ed_error::EdResult;
|
|||
use crate::editor::ed_error::MissingParent;
|
||||
use crate::editor::ed_error::RecordWithoutFields;
|
||||
use crate::editor::markup::attribute::Attributes;
|
||||
use crate::editor::markup::common_nodes::new_blank_mn;
|
||||
use crate::editor::markup::common_nodes::new_left_accolade_mn;
|
||||
use crate::editor::markup::common_nodes::new_right_accolade_mn;
|
||||
use crate::editor::markup::nodes;
|
||||
use crate::editor::markup::nodes::MarkupNode;
|
||||
use crate::editor::mvc::app_update::InputOutcome;
|
||||
|
@ -12,6 +15,7 @@ use crate::editor::slow_pool::MarkNodeId;
|
|||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::editor::util::index_of;
|
||||
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
||||
use crate::lang::parse::ASTNodeId;
|
||||
use crate::lang::pool::{PoolStr, PoolVec};
|
||||
use crate::ui::text::text_pos::TextPos;
|
||||
use snafu::OptionExt;
|
||||
|
@ -26,61 +30,44 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
|||
} = get_node_context(ed_model)?;
|
||||
|
||||
let is_blank_node = curr_mark_node.is_blank();
|
||||
let curr_mark_node_nls = curr_mark_node.get_newlines_at_end();
|
||||
|
||||
let ast_pool = &mut ed_model.module.env.pool;
|
||||
let expr2_node = Expr2::EmptyRecord;
|
||||
|
||||
let mark_node_pool = &mut ed_model.markup_node_pool;
|
||||
ast_pool.set(ast_node_id.to_expr_id()?, expr2_node);
|
||||
|
||||
ast_pool.set(ast_node_id, expr2_node);
|
||||
let left_bracket_node_id = ed_model.add_mark_node(new_left_accolade_mn(
|
||||
ast_node_id.to_expr_id()?,
|
||||
Some(curr_mark_node_id),
|
||||
));
|
||||
|
||||
let left_bracket_node = MarkupNode::Text {
|
||||
content: nodes::LEFT_ACCOLADE.to_owned(),
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one
|
||||
};
|
||||
|
||||
let left_bracket_node_id = mark_node_pool.add(left_bracket_node);
|
||||
|
||||
let right_bracket_node = MarkupNode::Text {
|
||||
content: nodes::RIGHT_ACCOLADE.to_owned(),
|
||||
ast_node_id,
|
||||
syn_high_style: HighlightStyle::Bracket,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one
|
||||
};
|
||||
|
||||
let right_bracket_node_id = mark_node_pool.add(right_bracket_node);
|
||||
let right_bracket_node_id = ed_model.add_mark_node(new_right_accolade_mn(
|
||||
ast_node_id.to_expr_id()?,
|
||||
Some(curr_mark_node_id),
|
||||
));
|
||||
|
||||
let nested_node = MarkupNode::Nested {
|
||||
ast_node_id,
|
||||
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
||||
parent_id_opt,
|
||||
newlines_at_end: curr_mark_node_nls,
|
||||
};
|
||||
|
||||
if is_blank_node {
|
||||
mark_node_pool.replace_node(curr_mark_node_id, nested_node);
|
||||
ed_model
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, nested_node);
|
||||
|
||||
// remove data corresponding to Blank node
|
||||
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
|
||||
ed_model.del_blank_expr_node(old_caret_pos)?;
|
||||
|
||||
ed_model.simple_move_carets_right(nodes::LEFT_ACCOLADE.len());
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
ed_model.insert_all_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
nodes::LEFT_ACCOLADE,
|
||||
left_bracket_node_id,
|
||||
)?;
|
||||
|
||||
ed_model.insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column + nodes::LEFT_ACCOLADE.len(),
|
||||
nodes::RIGHT_ACCOLADE,
|
||||
right_bracket_node_id,
|
||||
&[left_bracket_node_id, right_bracket_node_id],
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
@ -100,7 +87,7 @@ pub fn update_empty_record(
|
|||
if input_chars.all(|ch| ch.is_ascii_alphabetic())
|
||||
&& input_chars.all(|ch| ch.is_ascii_lowercase())
|
||||
{
|
||||
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
|
||||
let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id);
|
||||
|
||||
let NodeContext {
|
||||
old_caret_pos,
|
||||
|
@ -110,8 +97,8 @@ pub fn update_empty_record(
|
|||
ast_node_id,
|
||||
} = get_node_context(ed_model)?;
|
||||
|
||||
if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE
|
||||
&& curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE
|
||||
if prev_mark_node.get_content() == nodes::LEFT_ACCOLADE
|
||||
&& curr_mark_node.get_content() == nodes::RIGHT_ACCOLADE
|
||||
{
|
||||
// update AST
|
||||
let record_var = ed_model.module.env.var_store.fresh();
|
||||
|
@ -124,7 +111,11 @@ pub fn update_empty_record(
|
|||
|
||||
let new_ast_node = Expr2::Record { record_var, fields };
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, new_ast_node);
|
||||
|
||||
// update Markup
|
||||
|
||||
|
@ -134,12 +125,13 @@ pub fn update_empty_record(
|
|||
syn_high_style: HighlightStyle::RecordField,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
let record_field_node_id = ed_model.markup_node_pool.add(record_field_node);
|
||||
let record_field_node_id = ed_model.add_mark_node(record_field_node);
|
||||
|
||||
if let Some(parent_id) = parent_id_opt {
|
||||
let parent = ed_model.markup_node_pool.get_mut(parent_id);
|
||||
let parent = ed_model.mark_node_pool.get_mut(parent_id);
|
||||
|
||||
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
|
||||
|
||||
|
@ -155,11 +147,13 @@ pub fn update_empty_record(
|
|||
ed_model.simple_move_carets_right(1);
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
new_input,
|
||||
record_field_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
@ -183,23 +177,22 @@ pub fn update_record_colon(
|
|||
ast_node_id,
|
||||
} = get_node_context(ed_model)?;
|
||||
if let Some(parent_id) = parent_id_opt {
|
||||
let curr_ast_node = ed_model.module.env.pool.get(ast_node_id);
|
||||
let curr_ast_node = ed_model.module.env.pool.get(ast_node_id.to_expr_id()?);
|
||||
|
||||
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
|
||||
if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
|
||||
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
|
||||
let prev_mark_node = ed_model.mark_node_pool.get(prev_mark_node_id);
|
||||
|
||||
let prev_ast_node = ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.get(prev_mark_node.get_ast_node_id());
|
||||
match prev_mark_node.get_ast_node_id() {
|
||||
ASTNodeId::ADefId(_) => Ok(InputOutcome::Ignored),
|
||||
ASTNodeId::AExprId(prev_expr_id) => {
|
||||
let prev_expr = ed_model.module.env.pool.get(prev_expr_id);
|
||||
|
||||
// current and prev node should always point to record when in valid position to add ':'
|
||||
if matches!(prev_ast_node, Expr2::Record { .. })
|
||||
if matches!(prev_expr, Expr2::Record { .. })
|
||||
&& matches!(curr_ast_node, Expr2::Record { .. })
|
||||
{
|
||||
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
|
||||
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.mark_node_pool);
|
||||
|
||||
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
|
||||
|
||||
|
@ -213,11 +206,13 @@ pub fn update_record_colon(
|
|||
if ed_model.node_exists_at_caret() {
|
||||
let next_mark_node_id =
|
||||
ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?;
|
||||
let next_mark_node = ed_model.markup_node_pool.get(next_mark_node_id);
|
||||
if next_mark_node.get_content()? == nodes::RIGHT_ACCOLADE {
|
||||
let next_mark_node =
|
||||
ed_model.mark_node_pool.get(next_mark_node_id);
|
||||
if next_mark_node.get_content() == nodes::RIGHT_ACCOLADE {
|
||||
// update AST node
|
||||
let new_field_val = Expr2::Blank;
|
||||
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
|
||||
let new_field_val_id =
|
||||
ed_model.module.env.pool.add(new_field_val);
|
||||
|
||||
let first_field_mut = fields
|
||||
.iter_mut(ed_model.module.env.pool)
|
||||
|
@ -235,30 +230,31 @@ pub fn update_record_colon(
|
|||
|
||||
let record_colon_node = MarkupNode::Text {
|
||||
content: record_colon.to_owned(),
|
||||
ast_node_id: record_ast_node_id,
|
||||
ast_node_id: ASTNodeId::AExprId(record_ast_node_id),
|
||||
syn_high_style: HighlightStyle::Operator,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(parent_id),
|
||||
newlines_at_end: 0,
|
||||
};
|
||||
|
||||
let record_colon_node_id =
|
||||
ed_model.markup_node_pool.add(record_colon_node);
|
||||
ed_model.add_mark_node(record_colon_node);
|
||||
ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.get_mut(parent_id)
|
||||
.add_child_at_index(new_child_index, record_colon_node_id)?;
|
||||
|
||||
let record_blank_node = MarkupNode::Blank {
|
||||
ast_node_id: new_field_val_id,
|
||||
syn_high_style: HighlightStyle::Blank,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt: Some(parent_id),
|
||||
};
|
||||
.add_child_at_index(
|
||||
new_child_index,
|
||||
record_colon_node_id,
|
||||
)?;
|
||||
|
||||
let record_blank_node_id =
|
||||
ed_model.markup_node_pool.add(record_blank_node);
|
||||
ed_model.add_mark_node(new_blank_mn(
|
||||
ASTNodeId::AExprId(new_field_val_id),
|
||||
Some(parent_id),
|
||||
));
|
||||
|
||||
ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.get_mut(parent_id)
|
||||
.add_child_at_index(
|
||||
new_child_index + 1,
|
||||
|
@ -269,18 +265,10 @@ pub fn update_record_colon(
|
|||
ed_model.simple_move_carets_right(record_colon.len());
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
ed_model.insert_all_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
nodes::COLON,
|
||||
record_colon_node_id,
|
||||
)?;
|
||||
|
||||
ed_model.insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column + nodes::COLON.len(),
|
||||
nodes::BLANK_PLACEHOLDER,
|
||||
record_blank_node_id,
|
||||
&[record_colon_node_id, record_blank_node_id],
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
|
@ -296,6 +284,8 @@ pub fn update_record_colon(
|
|||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
|
@ -315,7 +305,7 @@ pub fn update_record_field(
|
|||
ed_model: &mut EdModel,
|
||||
) -> EdResult<InputOutcome> {
|
||||
// update MarkupNode
|
||||
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
|
||||
let curr_mark_node_mut = ed_model.mark_node_pool.get_mut(curr_mark_node_id);
|
||||
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||
let node_caret_offset = ed_model
|
||||
.grid_node_map
|
||||
|
@ -337,11 +327,13 @@ pub fn update_record_field(
|
|||
ed_model.simple_move_carets_right(new_input.len());
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
new_input,
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
// update AST Node
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::editor::mvc::ed_model::EdModel;
|
|||
use crate::editor::mvc::ed_update::get_node_context;
|
||||
use crate::editor::mvc::ed_update::NodeContext;
|
||||
use crate::editor::syntax_highlight::HighlightStyle;
|
||||
use crate::lang::ast::update_str_expr;
|
||||
use crate::lang::ast::ArrString;
|
||||
use crate::lang::ast::Expr2;
|
||||
use crate::lang::pool::PoolStr;
|
||||
|
@ -27,7 +28,7 @@ pub fn update_small_string(
|
|||
let new_input = &new_char.to_string();
|
||||
|
||||
// update markup
|
||||
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
|
||||
let curr_mark_node_mut = ed_model.mark_node_pool.get_mut(curr_mark_node_id);
|
||||
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||
let node_caret_offset = ed_model
|
||||
.grid_node_map
|
||||
|
@ -36,7 +37,7 @@ pub fn update_small_string(
|
|||
if node_caret_offset != 0 && node_caret_offset < content_str_mut.len() {
|
||||
if old_array_str.len() < ArrString::capacity() {
|
||||
if let Expr2::SmallStr(ref mut mut_array_str) =
|
||||
ed_model.module.env.pool.get_mut(ast_node_id)
|
||||
ed_model.module.env.pool.get_mut(ast_node_id.to_expr_id()?)
|
||||
{
|
||||
// safe because we checked the length
|
||||
unsafe {
|
||||
|
@ -51,17 +52,23 @@ pub fn update_small_string(
|
|||
|
||||
let new_ast_node = Expr2::Str(PoolStr::new(&new_str, ed_model.module.env.pool));
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, new_ast_node);
|
||||
}
|
||||
|
||||
content_str_mut.insert_str(node_caret_offset, new_input);
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
new_input,
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
// update caret
|
||||
|
@ -73,11 +80,7 @@ pub fn update_small_string(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_string(
|
||||
new_input: &str,
|
||||
old_pool_str: &PoolStr,
|
||||
ed_model: &mut EdModel,
|
||||
) -> EdResult<InputOutcome> {
|
||||
pub fn update_string(new_char: char, ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
||||
let NodeContext {
|
||||
old_caret_pos,
|
||||
curr_mark_node_id,
|
||||
|
@ -87,31 +90,32 @@ pub fn update_string(
|
|||
} = get_node_context(ed_model)?;
|
||||
|
||||
// update markup
|
||||
let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id);
|
||||
let curr_mark_node_mut = ed_model.mark_node_pool.get_mut(curr_mark_node_id);
|
||||
let content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||
let node_caret_offset = ed_model
|
||||
.grid_node_map
|
||||
.get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?;
|
||||
|
||||
if node_caret_offset != 0 && node_caret_offset < content_str_mut.len() {
|
||||
content_str_mut.insert_str(node_caret_offset, new_input);
|
||||
content_str_mut.insert(node_caret_offset, new_char);
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
new_input,
|
||||
&new_char.to_string(),
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
// update ast
|
||||
let mut new_string = old_pool_str.as_str(ed_model.module.env.pool).to_owned();
|
||||
new_string.push_str(new_input);
|
||||
|
||||
let new_pool_str = PoolStr::new(&new_string, &mut ed_model.module.env.pool);
|
||||
let new_ast_node = Expr2::Str(new_pool_str);
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
|
||||
update_str_expr(
|
||||
ast_node_id.to_expr_id()?,
|
||||
new_char,
|
||||
node_caret_offset - 1, // -1 because offset was calculated with quotes
|
||||
&mut ed_model.module.env.pool,
|
||||
)?;
|
||||
|
||||
// update caret
|
||||
ed_model.simple_move_carets_right(1);
|
||||
|
@ -133,8 +137,13 @@ pub fn start_new_string(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
|||
|
||||
if curr_mark_node.is_blank() {
|
||||
let new_expr2_node = Expr2::SmallStr(arraystring::ArrayString::new());
|
||||
let curr_mark_node_nls = curr_mark_node.get_newlines_at_end();
|
||||
|
||||
ed_model.module.env.pool.set(ast_node_id, new_expr2_node);
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_expr_id()?, new_expr2_node);
|
||||
|
||||
let new_string_node = MarkupNode::Text {
|
||||
content: nodes::STRING_QUOTES.to_owned(),
|
||||
|
@ -142,21 +151,24 @@ pub fn start_new_string(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
|||
syn_high_style: HighlightStyle::String,
|
||||
attributes: Attributes::new(),
|
||||
parent_id_opt,
|
||||
newlines_at_end: curr_mark_node_nls,
|
||||
};
|
||||
|
||||
ed_model
|
||||
.markup_node_pool
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, new_string_node);
|
||||
|
||||
// remove data corresponding to Blank node
|
||||
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
|
||||
ed_model.del_blank_expr_node(old_caret_pos)?;
|
||||
|
||||
// update GridNodeMap and CodeLines
|
||||
ed_model.insert_between_line(
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
nodes::STRING_QUOTES,
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
ed_model.simple_move_carets_right(1);
|
||||
|
|
206
editor/src/editor/mvc/tld_value_update.rs
Normal file
206
editor/src/editor/mvc/tld_value_update.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
use roc_module::symbol::{Interns, Symbol};
|
||||
|
||||
use crate::{
|
||||
editor::{
|
||||
ed_error::{EdResult, FailedToUpdateIdentIdName, KeyNotFound},
|
||||
markup::{
|
||||
attribute::Attributes,
|
||||
common_nodes::{new_blank_mn_w_nls, new_equals_mn},
|
||||
nodes::{set_parent_for_all, MarkupNode},
|
||||
},
|
||||
slow_pool::{MarkNodeId, SlowPool},
|
||||
syntax_highlight::HighlightStyle,
|
||||
},
|
||||
lang::{
|
||||
ast::{Def2, Expr2},
|
||||
expr::Env,
|
||||
parse::ASTNodeId,
|
||||
pattern::{get_identifier_string, Pattern2},
|
||||
pool::NodeId,
|
||||
},
|
||||
ui::text::text_pos::TextPos,
|
||||
};
|
||||
|
||||
use super::{
|
||||
app_update::InputOutcome,
|
||||
ed_model::EdModel,
|
||||
ed_update::{get_node_context, NodeContext},
|
||||
};
|
||||
|
||||
// Top Level Defined Value. example: `main = "Hello, World!"`
|
||||
|
||||
pub fn tld_mark_node<'a>(
|
||||
identifier_id: NodeId<Pattern2>,
|
||||
expr_mark_node_id: MarkNodeId,
|
||||
ast_node_id: ASTNodeId,
|
||||
mark_node_pool: &mut SlowPool,
|
||||
env: &Env<'a>,
|
||||
interns: &Interns,
|
||||
) -> EdResult<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)
|
||||
}
|
||||
|
||||
pub fn start_new_tld_value(ed_model: &mut EdModel, new_char: &char) -> EdResult<InputOutcome> {
|
||||
let NodeContext {
|
||||
old_caret_pos,
|
||||
curr_mark_node_id,
|
||||
curr_mark_node: _,
|
||||
parent_id_opt: _,
|
||||
ast_node_id,
|
||||
} = get_node_context(ed_model)?;
|
||||
|
||||
let val_expr_node = Expr2::Blank;
|
||||
let val_expr_id = ed_model.module.env.pool.add(val_expr_node);
|
||||
|
||||
let val_expr_mn = new_blank_mn_w_nls(ASTNodeId::AExprId(val_expr_id), None, 0);
|
||||
let val_expr_mn_id = ed_model.mark_node_pool.add(val_expr_mn);
|
||||
|
||||
let val_name_string = new_char.to_string();
|
||||
|
||||
let ident_id = ed_model
|
||||
.module
|
||||
.env
|
||||
.ident_ids
|
||||
.add(val_name_string.clone().into());
|
||||
|
||||
let module_ident_ids_opt = ed_model
|
||||
.loaded_module
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&ed_model.module.env.home);
|
||||
|
||||
if let Some(module_ident_ids_ref) = module_ident_ids_opt {
|
||||
// this might create different IdentId for interns and env.ident_ids which may be a problem
|
||||
module_ident_ids_ref.add(val_name_string.into());
|
||||
} else {
|
||||
KeyNotFound {
|
||||
key_str: format!("{:?}", ed_model.module.env.home),
|
||||
}
|
||||
.fail()?
|
||||
}
|
||||
|
||||
let val_symbol = Symbol::new(ed_model.module.env.home, ident_id);
|
||||
|
||||
let patt2 = Pattern2::Identifier(val_symbol);
|
||||
let patt2_id = ed_model.module.env.pool.add(patt2);
|
||||
|
||||
let tld_mark_node = tld_mark_node(
|
||||
patt2_id,
|
||||
val_expr_mn_id,
|
||||
ast_node_id,
|
||||
&mut ed_model.mark_node_pool,
|
||||
&ed_model.module.env,
|
||||
&ed_model.loaded_module.interns,
|
||||
)?;
|
||||
|
||||
let new_ast_node = Def2::ValueDef {
|
||||
identifier_id: patt2_id,
|
||||
expr_id: val_expr_id,
|
||||
};
|
||||
|
||||
ed_model
|
||||
.module
|
||||
.env
|
||||
.pool
|
||||
.set(ast_node_id.to_def_id()?, new_ast_node);
|
||||
|
||||
ed_model
|
||||
.mark_node_pool
|
||||
.replace_node(curr_mark_node_id, tld_mark_node);
|
||||
|
||||
set_parent_for_all(curr_mark_node_id, &mut ed_model.mark_node_pool);
|
||||
|
||||
// remove data corresponding to old Blank node
|
||||
ed_model.del_line(old_caret_pos.line + 1)?;
|
||||
ed_model.del_line(old_caret_pos.line)?;
|
||||
|
||||
let char_len = 1;
|
||||
ed_model.simple_move_carets_right(char_len);
|
||||
|
||||
let mut curr_line = old_caret_pos.line;
|
||||
let mut curr_column = old_caret_pos.column;
|
||||
|
||||
EdModel::insert_mark_node_between_line(
|
||||
&mut curr_line,
|
||||
&mut curr_column,
|
||||
curr_mark_node_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
&ed_model.mark_node_pool,
|
||||
)?;
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
}
|
||||
|
||||
pub fn update_tld_val_name(
|
||||
val_name_mn_id: MarkNodeId,
|
||||
old_caret_pos: TextPos,
|
||||
ed_model: &mut EdModel,
|
||||
new_char: &char,
|
||||
) -> EdResult<InputOutcome> {
|
||||
if new_char.is_ascii_alphanumeric() {
|
||||
// update markup
|
||||
let val_name_mn_mut = ed_model.mark_node_pool.get_mut(val_name_mn_id);
|
||||
let content_str_mut = val_name_mn_mut.get_content_mut()?;
|
||||
|
||||
let old_val_name = content_str_mut.clone();
|
||||
|
||||
let node_caret_offset = ed_model
|
||||
.grid_node_map
|
||||
.get_offset_to_node_id(old_caret_pos, val_name_mn_id)?;
|
||||
|
||||
if node_caret_offset <= content_str_mut.len() {
|
||||
content_str_mut.insert(node_caret_offset, *new_char);
|
||||
|
||||
let update_val_name_res = ed_model
|
||||
.module
|
||||
.env
|
||||
.ident_ids
|
||||
.update_key(&old_val_name, content_str_mut);
|
||||
|
||||
if let Err(err_str) = update_val_name_res {
|
||||
FailedToUpdateIdentIdName { err_str }.fail()?;
|
||||
}
|
||||
|
||||
EdModel::insert_between_line(
|
||||
old_caret_pos.line,
|
||||
old_caret_pos.column,
|
||||
&new_char.to_string(),
|
||||
val_name_mn_id,
|
||||
&mut ed_model.grid_node_map,
|
||||
&mut ed_model.code_lines,
|
||||
)?;
|
||||
|
||||
ed_model.simple_move_caret_right(old_caret_pos, 1);
|
||||
|
||||
Ok(InputOutcome::Accepted)
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
} else {
|
||||
Ok(InputOutcome::Ignored)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use super::markup::nodes::{MarkupNode, BLANK_PLACEHOLDER};
|
||||
use super::slow_pool::MarkNodeId;
|
||||
use crate::editor::mvc::ed_view::RenderedWgpu;
|
||||
use crate::editor::slow_pool::SlowPool;
|
||||
use crate::editor::{ed_error::EdResult, theme::EdTheme, util::map_get};
|
||||
|
@ -10,37 +11,49 @@ use winit::dpi::PhysicalSize;
|
|||
use crate::{editor::config::Config, graphics::colors};
|
||||
|
||||
pub fn build_code_graphics<'a>(
|
||||
markup_node: &'a MarkupNode,
|
||||
markup_ids: &[MarkNodeId],
|
||||
size: &PhysicalSize<u32>,
|
||||
txt_coords: Vector2<f32>,
|
||||
config: &Config,
|
||||
glyph_dim_rect: Rect,
|
||||
markup_node_pool: &'a SlowPool,
|
||||
mark_node_pool: &'a SlowPool,
|
||||
) -> EdResult<RenderedWgpu> {
|
||||
let area_bounds = (size.width as f32, size.height as f32);
|
||||
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
||||
let mut rendered_wgpu = RenderedWgpu::new();
|
||||
|
||||
let (glyph_text_vec, rects) = markup_to_wgpu(
|
||||
markup_node,
|
||||
let mut all_glyph_text_vec = vec![];
|
||||
let mut all_rects = vec![];
|
||||
let mut txt_row_col = (0, 0);
|
||||
|
||||
for markup_id in markup_ids.iter() {
|
||||
let mark_node = mark_node_pool.get(*markup_id);
|
||||
|
||||
let (mut glyph_text_vec, mut rects) = markup_to_wgpu(
|
||||
mark_node,
|
||||
&CodeStyle {
|
||||
ed_theme: &config.ed_theme,
|
||||
font_size: config.code_font_size,
|
||||
txt_coords,
|
||||
glyph_dim_rect,
|
||||
},
|
||||
markup_node_pool,
|
||||
&mut txt_row_col,
|
||||
mark_node_pool,
|
||||
)?;
|
||||
|
||||
all_glyph_text_vec.append(&mut glyph_text_vec);
|
||||
all_rects.append(&mut rects)
|
||||
}
|
||||
|
||||
let section = gr_text::owned_section_from_glyph_texts(
|
||||
glyph_text_vec,
|
||||
all_glyph_text_vec,
|
||||
txt_coords.into(),
|
||||
area_bounds,
|
||||
layout,
|
||||
);
|
||||
|
||||
rendered_wgpu.add_rects(rects);
|
||||
rendered_wgpu.add_text(section);
|
||||
rendered_wgpu.add_rects_behind(all_rects); // currently only rects for Blank
|
||||
rendered_wgpu.add_text_behind(section);
|
||||
|
||||
Ok(rendered_wgpu)
|
||||
}
|
||||
|
@ -55,51 +68,57 @@ struct CodeStyle<'a> {
|
|||
fn markup_to_wgpu<'a>(
|
||||
markup_node: &'a MarkupNode,
|
||||
code_style: &CodeStyle,
|
||||
markup_node_pool: &'a SlowPool,
|
||||
txt_row_col: &mut (usize, usize),
|
||||
mark_node_pool: &'a SlowPool,
|
||||
) -> EdResult<(Vec<glyph_brush::OwnedText>, Vec<Rect>)> {
|
||||
let mut wgpu_texts: Vec<glyph_brush::OwnedText> = Vec::new();
|
||||
let mut rects: Vec<Rect> = Vec::new();
|
||||
|
||||
let mut txt_row_col = (0, 0);
|
||||
|
||||
markup_to_wgpu_helper(
|
||||
markup_node,
|
||||
&mut wgpu_texts,
|
||||
&mut rects,
|
||||
code_style,
|
||||
&mut txt_row_col,
|
||||
markup_node_pool,
|
||||
txt_row_col,
|
||||
mark_node_pool,
|
||||
)?;
|
||||
|
||||
Ok((wgpu_texts, rects))
|
||||
}
|
||||
|
||||
// TODO use text_row
|
||||
fn markup_to_wgpu_helper<'a>(
|
||||
markup_node: &'a MarkupNode,
|
||||
wgpu_texts: &mut Vec<glyph_brush::OwnedText>,
|
||||
rects: &mut Vec<Rect>,
|
||||
code_style: &CodeStyle,
|
||||
txt_row_col: &mut (usize, usize),
|
||||
markup_node_pool: &'a SlowPool,
|
||||
mark_node_pool: &'a SlowPool,
|
||||
) -> EdResult<()> {
|
||||
match markup_node {
|
||||
MarkupNode::Nested {
|
||||
ast_node_id: _,
|
||||
children_ids,
|
||||
parent_id_opt: _,
|
||||
newlines_at_end,
|
||||
} => {
|
||||
for child_id in children_ids.iter() {
|
||||
let child = markup_node_pool.get(*child_id);
|
||||
let child = mark_node_pool.get(*child_id);
|
||||
markup_to_wgpu_helper(
|
||||
child,
|
||||
wgpu_texts,
|
||||
rects,
|
||||
code_style,
|
||||
txt_row_col,
|
||||
markup_node_pool,
|
||||
mark_node_pool,
|
||||
)?;
|
||||
}
|
||||
|
||||
for _ in 0..*newlines_at_end {
|
||||
wgpu_texts.push(newline(code_style.font_size));
|
||||
|
||||
txt_row_col.0 += 1;
|
||||
txt_row_col.1 = 0;
|
||||
}
|
||||
}
|
||||
MarkupNode::Text {
|
||||
content,
|
||||
|
@ -107,14 +126,23 @@ fn markup_to_wgpu_helper<'a>(
|
|||
syn_high_style,
|
||||
attributes: _,
|
||||
parent_id_opt: _,
|
||||
newlines_at_end,
|
||||
} => {
|
||||
let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, syn_high_style)?;
|
||||
|
||||
let glyph_text = glyph_brush::OwnedText::new(content)
|
||||
let full_content = markup_node.get_full_content();
|
||||
|
||||
let glyph_text = glyph_brush::OwnedText::new(full_content)
|
||||
.with_color(colors::to_slice(*highlight_color))
|
||||
.with_scale(code_style.font_size);
|
||||
|
||||
txt_row_col.1 += content.len();
|
||||
|
||||
for _ in 0..*newlines_at_end {
|
||||
txt_row_col.0 += 1;
|
||||
txt_row_col.1 = 0;
|
||||
}
|
||||
|
||||
wgpu_texts.push(glyph_text);
|
||||
}
|
||||
MarkupNode::Blank {
|
||||
|
@ -122,8 +150,11 @@ fn markup_to_wgpu_helper<'a>(
|
|||
attributes: _,
|
||||
syn_high_style,
|
||||
parent_id_opt: _,
|
||||
newlines_at_end,
|
||||
} => {
|
||||
let glyph_text = glyph_brush::OwnedText::new(BLANK_PLACEHOLDER)
|
||||
let full_content = markup_node.get_full_content();
|
||||
|
||||
let glyph_text = glyph_brush::OwnedText::new(full_content)
|
||||
.with_color(colors::to_slice(colors::WHITE))
|
||||
.with_scale(code_style.font_size);
|
||||
|
||||
|
@ -132,7 +163,7 @@ fn markup_to_wgpu_helper<'a>(
|
|||
let char_width = code_style.glyph_dim_rect.width;
|
||||
let char_height = code_style.glyph_dim_rect.height;
|
||||
|
||||
let hole_rect = Rect {
|
||||
let blank_rect = Rect {
|
||||
top_left_coords: (
|
||||
code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width,
|
||||
code_style.txt_coords.y
|
||||
|
@ -144,12 +175,21 @@ fn markup_to_wgpu_helper<'a>(
|
|||
height: char_height,
|
||||
color: *highlight_color,
|
||||
};
|
||||
rects.push(hole_rect);
|
||||
rects.push(blank_rect);
|
||||
|
||||
txt_row_col.1 += BLANK_PLACEHOLDER.len();
|
||||
wgpu_texts.push(glyph_text);
|
||||
|
||||
for _ in 0..*newlines_at_end {
|
||||
txt_row_col.0 += 1;
|
||||
txt_row_col.1 = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn newline(font_size: f32) -> glyph_brush::OwnedText {
|
||||
glyph_brush::OwnedText::new("\n").with_scale(font_size)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::editor::mvc::ed_model::EdModel;
|
|||
use crate::graphics::colors;
|
||||
use crate::graphics::colors::from_hsb;
|
||||
use crate::graphics::primitives::text as gr_text;
|
||||
use crate::lang::ast::expr2_to_string;
|
||||
use crate::lang::ast::def2_to_string;
|
||||
use cgmath::Vector2;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
|
@ -19,36 +19,49 @@ pub fn build_debug_graphics(
|
|||
let area_bounds = (size.width as f32, size.height as f32);
|
||||
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
||||
|
||||
let debug_txt_coords: Vector2<f32> = (txt_coords.x, txt_coords.y * 3.0).into();
|
||||
let debug_txt_coords: Vector2<f32> = (txt_coords.x * 20.0, txt_coords.y).into();
|
||||
|
||||
let carets_text =
|
||||
glyph_brush::OwnedText::new(format!("carets: {:?}\n\n", ed_model.get_carets()))
|
||||
.with_color(colors::to_slice(from_hsb(0, 0, 100)))
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let grid_node_map_text = glyph_brush::OwnedText::new(format!("{}", ed_model.grid_node_map))
|
||||
.with_color(colors::to_slice(from_hsb(20, 41, 100)))
|
||||
.with_scale(config.code_font_size);
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let code_lines_text = glyph_brush::OwnedText::new(format!("{}", ed_model.code_lines))
|
||||
.with_color(colors::to_slice(from_hsb(0, 49, 96)))
|
||||
.with_scale(config.code_font_size);
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let mark_node_tree_text = glyph_brush::OwnedText::new(tree_as_string(
|
||||
ed_model.markup_root_id,
|
||||
&ed_model.markup_node_pool,
|
||||
))
|
||||
let mut mark_node_trees_string = "\nmark node trees:".to_owned();
|
||||
|
||||
for mark_id in ed_model.markup_ids[1..].iter() {
|
||||
// 1.. -> skip header
|
||||
mark_node_trees_string.push_str(&tree_as_string(*mark_id, &ed_model.mark_node_pool));
|
||||
}
|
||||
|
||||
let mark_node_tree_text = glyph_brush::OwnedText::new(mark_node_trees_string)
|
||||
.with_color(colors::to_slice(from_hsb(266, 31, 96)))
|
||||
.with_scale(config.code_font_size);
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let mark_node_pool_text = glyph_brush::OwnedText::new(format!("{}", ed_model.markup_node_pool))
|
||||
let mark_node_pool_text = glyph_brush::OwnedText::new(format!("{}", ed_model.mark_node_pool))
|
||||
.with_color(colors::to_slice(from_hsb(110, 45, 82)))
|
||||
.with_scale(config.code_font_size);
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let ast_node_text = glyph_brush::OwnedText::new(format!(
|
||||
"\n\n(ast_root)\n{}",
|
||||
expr2_to_string(ed_model.module.ast_root_id, ed_model.module.env.pool)
|
||||
))
|
||||
let mut ast_node_text_str = "AST:\n".to_owned();
|
||||
|
||||
for def_id in ed_model.module.ast.def_ids.iter() {
|
||||
ast_node_text_str.push_str(&def2_to_string(*def_id, ed_model.module.env.pool))
|
||||
}
|
||||
|
||||
let ast_node_text = glyph_brush::OwnedText::new(ast_node_text_str)
|
||||
.with_color(colors::to_slice(from_hsb(211, 80, 100)))
|
||||
.with_scale(config.code_font_size);
|
||||
.with_scale(config.debug_font_size);
|
||||
|
||||
let section = gr_text::owned_section_from_glyph_texts(
|
||||
vec![
|
||||
carets_text,
|
||||
grid_node_map_text,
|
||||
code_lines_text,
|
||||
mark_node_tree_text,
|
||||
|
|
|
@ -1,3 +1,27 @@
|
|||
pub const NOTHING_OPENED: &str = "Opening files is not yet supported, execute `cargo run edit` from the root folder of the repo to try the editor.";
|
||||
pub const START_TIP: &str =
|
||||
"Start by typing '[', '{', '\"' or a number.\nInput chars that would create parse errors will be ignored.";
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub const NOTHING_OPENED: &str =
|
||||
"Execute `cargo run edit` from the root folder of the repo to try the editor.";
|
||||
|
||||
pub const START_TIP: &str = r#"Currently supported: lists, records, string, numbers and value definitions.
|
||||
|
||||
Use `Ctrl+Shift+Up` or `Cmd+Shift+Up` to select surrounding expression.
|
||||
Use backspace after `Ctrl+Shift+Up` to delete the selected expression.
|
||||
|
||||
`Ctrl+S` or `Cmd+S` to save.
|
||||
`Ctrl+R` to run.
|
||||
|
||||
Input chars that would create parse errors or change formatting will be ignored.
|
||||
For convenience and consistency, there is only one way to format roc.
|
||||
"#;
|
||||
|
||||
pub const HELLO_WORLD: &str = r#"
|
||||
app "test-app"
|
||||
packages { base: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
|
||||
main = "Hello, world!"
|
||||
|
||||
|
||||
"#;
|
||||
|
|
|
@ -63,7 +63,7 @@ impl fmt::Display for SlowPool {
|
|||
"{}: {} ({}) ast_id {:?} {}",
|
||||
index,
|
||||
node.node_type_as_string(),
|
||||
node.get_content().unwrap_or_else(|_| "".to_string()),
|
||||
node.get_content(),
|
||||
ast_node_id.parse::<usize>().unwrap(),
|
||||
child_str
|
||||
)?;
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub const CODE_TXT_XY: (f32, f32) = (40.0, 130.0);
|
||||
pub const CODE_TXT_XY: (f32, f32) = (40.0, 350.0);
|
||||
|
|
|
@ -14,6 +14,8 @@ pub enum HighlightStyle {
|
|||
PackageRelated, // app, packages, imports, exposes, provides...
|
||||
Variable,
|
||||
RecordField,
|
||||
Import,
|
||||
Provides,
|
||||
Blank,
|
||||
}
|
||||
|
||||
|
@ -31,6 +33,8 @@ pub fn default_highlight_map() -> HashMap<HighlightStyle, RgbaTup> {
|
|||
(PackageRelated, gr_colors::WHITE),
|
||||
(Variable, gr_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
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
use crate::editor::ed_error::{EdResult, UnexpectedASTNode};
|
||||
use crate::lang::pattern::{Pattern2, PatternId};
|
||||
use crate::lang::pool::Pool;
|
||||
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
|
||||
|
@ -222,6 +223,17 @@ pub enum Expr2 {
|
|||
RuntimeError(/* TODO make a version of RuntimeError that fits in 15B */),
|
||||
}
|
||||
|
||||
// A top level definition, not inside a function. For example: `main = "Hello, world!"`
|
||||
#[derive(Debug)]
|
||||
pub enum Def2 {
|
||||
// ValueDef example: `main = "Hello, world!"`. identifier -> `main`, expr -> "Hello, world!"
|
||||
ValueDef {
|
||||
identifier_id: NodeId<Pattern2>,
|
||||
expr_id: NodeId<Expr2>,
|
||||
},
|
||||
Blank,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValueDef {
|
||||
WithAnnotation {
|
||||
|
@ -267,6 +279,48 @@ impl ShallowClone for ValueDef {
|
|||
}
|
||||
}
|
||||
|
||||
impl ValueDef {
|
||||
pub fn get_expr_id(&self) -> ExprId {
|
||||
match self {
|
||||
ValueDef::WithAnnotation { expr_id, .. } => *expr_id,
|
||||
ValueDef::NoAnnotation { expr_id, .. } => *expr_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pattern_id(&self) -> NodeId<Pattern2> {
|
||||
match self {
|
||||
ValueDef::WithAnnotation { pattern_id, .. } => *pattern_id,
|
||||
ValueDef::NoAnnotation { pattern_id, .. } => *pattern_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_def_to_string(val_def: &ValueDef, pool: &Pool) -> String {
|
||||
match val_def {
|
||||
ValueDef::WithAnnotation {
|
||||
pattern_id,
|
||||
expr_id,
|
||||
type_id,
|
||||
rigids,
|
||||
expr_var,
|
||||
} => {
|
||||
format!("WithAnnotation {{ pattern_id: {:?}, expr_id: {:?}, type_id: {:?}, rigids: {:?}, expr_var: {:?}}}", pool.get(*pattern_id), expr2_to_string(*expr_id, pool), pool.get(*type_id), rigids, expr_var)
|
||||
}
|
||||
ValueDef::NoAnnotation {
|
||||
pattern_id,
|
||||
expr_id,
|
||||
expr_var,
|
||||
} => {
|
||||
format!(
|
||||
"NoAnnotation {{ pattern_id: {:?}, expr_id: {:?}, expr_var: {:?}}}",
|
||||
pool.get(*pattern_id),
|
||||
expr2_to_string(*expr_id, pool),
|
||||
expr_var
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FunctionDef {
|
||||
WithAnnotation {
|
||||
|
@ -402,7 +456,11 @@ pub struct WhenBranch {
|
|||
// TODO make the inner types private?
|
||||
pub type ExprId = NodeId<Expr2>;
|
||||
|
||||
pub type DefId = NodeId<Def2>;
|
||||
|
||||
use RecordField::*;
|
||||
|
||||
use super::parse::ASTNodeId;
|
||||
impl RecordField {
|
||||
pub fn get_record_field_var(&self) -> &Variable {
|
||||
match self {
|
||||
|
@ -437,6 +495,13 @@ impl RecordField {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ast_node_to_string(node_id: ASTNodeId, pool: &Pool) -> String {
|
||||
match node_id {
|
||||
ASTNodeId::ADefId(def_id) => def2_to_string(def_id, pool),
|
||||
ASTNodeId::AExprId(expr_id) => expr2_to_string(expr_id, pool),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr2_to_string(node_id: ExprId, pool: &Pool) -> String {
|
||||
let mut full_string = String::new();
|
||||
let expr2 = pool.get(node_id);
|
||||
|
@ -550,16 +615,118 @@ fn expr2_to_string_helper(
|
|||
Expr2::SmallInt { text, .. } => {
|
||||
out_string.push_str(&format!("SmallInt({})", text.as_str(pool)));
|
||||
}
|
||||
Expr2::LetValue {
|
||||
def_id, body_id, ..
|
||||
} => {
|
||||
out_string.push_str(&format!(
|
||||
"LetValue(def_id: >>{:?}), body_id: >>{:?})",
|
||||
value_def_to_string(pool.get(*def_id), pool),
|
||||
pool.get(*body_id)
|
||||
));
|
||||
}
|
||||
other => todo!("Implement for {:?}", other),
|
||||
}
|
||||
|
||||
out_string.push('\n');
|
||||
}
|
||||
|
||||
pub fn def2_to_string(node_id: DefId, pool: &Pool) -> String {
|
||||
let mut full_string = String::new();
|
||||
let def2 = pool.get(node_id);
|
||||
|
||||
match def2 {
|
||||
Def2::ValueDef {
|
||||
identifier_id,
|
||||
expr_id,
|
||||
} => {
|
||||
full_string.push_str(&format!(
|
||||
"Def2::ValueDef(identifier_id: >>{:?}), expr_id: >>{:?})",
|
||||
pool.get(*identifier_id),
|
||||
expr2_to_string(*expr_id, pool)
|
||||
));
|
||||
}
|
||||
Def2::Blank => {
|
||||
full_string.push_str("Def2::Blank");
|
||||
}
|
||||
}
|
||||
|
||||
full_string
|
||||
}
|
||||
|
||||
fn var_to_string(some_var: &Variable, indent_level: usize) -> String {
|
||||
format!("{}Var({:?})\n", get_spacing(indent_level + 1), some_var)
|
||||
}
|
||||
|
||||
// get string from SmallStr or Str
|
||||
pub fn get_string_from_expr2(node_id: ExprId, pool: &Pool) -> EdResult<String> {
|
||||
match pool.get(node_id) {
|
||||
Expr2::SmallStr(arr_string) => Ok(arr_string.as_str().to_string()),
|
||||
Expr2::Str(pool_str) => Ok(pool_str.as_str(pool).to_owned()),
|
||||
other => UnexpectedASTNode {
|
||||
required_node_type: "SmallStr or Str",
|
||||
encountered_node_type: format!("{:?}", other),
|
||||
}
|
||||
.fail()?,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_str_expr(
|
||||
node_id: ExprId,
|
||||
new_char: char,
|
||||
insert_index: usize,
|
||||
pool: &mut Pool,
|
||||
) -> EdResult<()> {
|
||||
let str_expr = pool.get_mut(node_id);
|
||||
|
||||
enum Either {
|
||||
MyString(String),
|
||||
MyPoolStr(PoolStr),
|
||||
Done,
|
||||
}
|
||||
|
||||
let insert_either = match str_expr {
|
||||
Expr2::SmallStr(arr_string) => {
|
||||
let insert_res = arr_string.try_insert(insert_index as u8, new_char);
|
||||
|
||||
match insert_res {
|
||||
Ok(_) => Either::Done,
|
||||
_ => {
|
||||
let mut new_string = arr_string.as_str().to_string();
|
||||
new_string.insert(insert_index, new_char);
|
||||
|
||||
Either::MyString(new_string)
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr2::Str(old_pool_str) => Either::MyPoolStr(*old_pool_str),
|
||||
other => UnexpectedASTNode {
|
||||
required_node_type: "SmallStr or Str",
|
||||
encountered_node_type: format!("{:?}", other),
|
||||
}
|
||||
.fail()?,
|
||||
};
|
||||
|
||||
match insert_either {
|
||||
Either::MyString(new_string) => {
|
||||
let new_pool_str = PoolStr::new(&new_string, pool);
|
||||
|
||||
pool.set(node_id, Expr2::Str(new_pool_str))
|
||||
}
|
||||
Either::MyPoolStr(old_pool_str) => {
|
||||
let mut new_string = old_pool_str.as_str(pool).to_owned();
|
||||
|
||||
new_string.insert(insert_index, new_char);
|
||||
|
||||
let new_pool_str = PoolStr::new(&new_string, pool);
|
||||
|
||||
pool.set(node_id, Expr2::Str(new_pool_str))
|
||||
}
|
||||
Either::Done => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_expr() {
|
||||
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
#![allow(unused_imports)]
|
||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use crate::lang::ast::{
|
||||
expr2_to_string, ClosureExtra, Expr2, ExprId, FloatVal, IntStyle, IntVal, RecordField,
|
||||
WhenBranch,
|
||||
expr2_to_string, value_def_to_string, ClosureExtra, Def2, Expr2, ExprId, FloatVal, IntStyle,
|
||||
IntVal, RecordField, ValueDef, WhenBranch,
|
||||
};
|
||||
use crate::lang::def::{
|
||||
canonicalize_defs, sort_can_defs, CanDefs, Declaration, Def, PendingDef, References,
|
||||
|
@ -288,6 +289,25 @@ pub fn to_expr_id<'a>(
|
|||
(env.add(expr, region), output)
|
||||
}
|
||||
|
||||
pub fn str_to_def2<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
region: Region,
|
||||
) -> Result<Vec<Def2>, SyntaxError<'a>> {
|
||||
match roc_parse::test_helpers::parse_defs_with(arena, input.trim()) {
|
||||
Ok(vec_loc_def) => Ok(defs_to_defs2(
|
||||
arena,
|
||||
env,
|
||||
scope,
|
||||
arena.alloc(vec_loc_def),
|
||||
region,
|
||||
)),
|
||||
Err(fail) => Err(fail),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str_to_expr2<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
|
@ -296,20 +316,23 @@ pub fn str_to_expr2<'a>(
|
|||
region: Region,
|
||||
) -> Result<(Expr2, self::Output), SyntaxError<'a>> {
|
||||
match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) {
|
||||
Ok(loc_expr) => {
|
||||
let desugared_loc_expr = desugar_expr(arena, arena.alloc(loc_expr));
|
||||
|
||||
Ok(to_expr2(
|
||||
env,
|
||||
scope,
|
||||
arena.alloc(desugared_loc_expr.value),
|
||||
region,
|
||||
))
|
||||
}
|
||||
Ok(loc_expr) => Ok(loc_expr_to_expr2(arena, loc_expr, env, scope, region)),
|
||||
Err(fail) => Err(fail),
|
||||
}
|
||||
}
|
||||
|
||||
fn loc_expr_to_expr2<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: Located<Expr<'a>>,
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
region: Region,
|
||||
) -> (Expr2, self::Output) {
|
||||
let desugared_loc_expr = desugar_expr(arena, arena.alloc(loc_expr));
|
||||
|
||||
to_expr2(env, scope, arena.alloc(desugared_loc_expr.value), region)
|
||||
}
|
||||
|
||||
pub fn to_expr2<'a>(
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
|
@ -967,6 +990,74 @@ pub fn to_expr2<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn defs_to_defs2<'a>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
parsed_defs: &'a BumpVec<roc_region::all::Loc<roc_parse::ast::Def<'a>>>,
|
||||
region: Region,
|
||||
) -> Vec<Def2> {
|
||||
use roc_parse::ast::Expr::*;
|
||||
|
||||
parsed_defs
|
||||
.iter()
|
||||
.map(|loc| to_def2_from_def(arena, env, scope, &loc.value, region))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn to_def2_from_def<'a>(
|
||||
arena: &'a Bump,
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
parsed_def: &'a roc_parse::ast::Def<'a>,
|
||||
region: Region,
|
||||
) -> Def2 {
|
||||
use roc_parse::ast::Def::*;
|
||||
|
||||
match parsed_def {
|
||||
SpaceBefore(inner_def, _) => to_def2_from_def(arena, env, scope, inner_def, region),
|
||||
SpaceAfter(inner_def, _) => to_def2_from_def(arena, env, scope, inner_def, region),
|
||||
Body(&loc_pattern, &loc_expr) => {
|
||||
// TODO loc_pattern use identifier
|
||||
let expr2 = loc_expr_to_expr2(arena, loc_expr, env, scope, region).0;
|
||||
let expr_id = env.pool.add(expr2);
|
||||
|
||||
use roc_parse::ast::Pattern::*;
|
||||
|
||||
match loc_pattern.value {
|
||||
Identifier(_) => {
|
||||
let (_, pattern2) = to_pattern2(
|
||||
env,
|
||||
scope,
|
||||
PatternType::TopLevelDef,
|
||||
&loc_pattern.value,
|
||||
region,
|
||||
);
|
||||
let pattern_id = env.pool.add(pattern2);
|
||||
|
||||
// TODO support with annotation
|
||||
Def2::ValueDef {
|
||||
identifier_id: pattern_id,
|
||||
expr_id,
|
||||
}
|
||||
}
|
||||
other => {
|
||||
unimplemented!(
|
||||
"I don't yet know how to convert the pattern {:?} into an expr2",
|
||||
other
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
unimplemented!(
|
||||
"I don't know how to make an expr2 from this def yet: {:?}",
|
||||
other
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_str_literal<'a>(
|
||||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
|
@ -1412,6 +1503,7 @@ fn decl_to_let(pool: &mut Pool, var_store: &mut VarStore, decl: Declaration, ret
|
|||
Def::AnnotationOnly { .. } => todo!(),
|
||||
Def::Value(value_def) => {
|
||||
let def_id = pool.add(value_def);
|
||||
|
||||
let body_id = pool.add(ret);
|
||||
|
||||
Expr2::LetValue {
|
||||
|
|
|
@ -3,7 +3,8 @@ pub mod constrain;
|
|||
mod def;
|
||||
pub mod expr;
|
||||
mod module;
|
||||
mod pattern;
|
||||
pub mod parse;
|
||||
pub mod pattern;
|
||||
pub mod pool;
|
||||
pub mod roc_file;
|
||||
pub mod scope;
|
||||
|
|
98
editor/src/lang/parse.rs
Normal file
98
editor/src/lang/parse.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
editor::ed_error::ASTNodeIdWithoutExprId, editor::ed_error::EdResult, lang::scope::Scope,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::parser::SyntaxError;
|
||||
use roc_region::all::Region;
|
||||
|
||||
use super::{
|
||||
ast::{DefId, Expr2, ExprId},
|
||||
expr::{str_to_def2, Env},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AST {
|
||||
pub header: AppHeader,
|
||||
pub def_ids: Vec<DefId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum ASTNodeId {
|
||||
ADefId(DefId),
|
||||
AExprId(ExprId),
|
||||
}
|
||||
|
||||
impl ASTNodeId {
|
||||
pub fn to_expr_id(&self) -> EdResult<ExprId> {
|
||||
match self {
|
||||
ASTNodeId::AExprId(expr_id) => Ok(*expr_id),
|
||||
_ => ASTNodeIdWithoutExprId { ast_node_id: *self }.fail()?,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_def_id(&self) -> EdResult<DefId> {
|
||||
match self {
|
||||
ASTNodeId::ADefId(def_id) => Ok(*def_id),
|
||||
_ => ASTNodeIdWithoutExprId { ast_node_id: *self }.fail()?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppHeader {
|
||||
pub app_name: String,
|
||||
pub packages_base: String,
|
||||
pub imports: Vec<String>,
|
||||
pub provides: Vec<String>,
|
||||
pub ast_node_id: ExprId, // TODO probably want to use HeaderId
|
||||
}
|
||||
|
||||
impl AST {
|
||||
pub fn parse_from_string<'a>(
|
||||
code_str: &'a str,
|
||||
env: &mut Env<'a>,
|
||||
ast_arena: &'a Bump,
|
||||
) -> Result<AST, SyntaxError<'a>> {
|
||||
let blank_line_indx = code_str
|
||||
.find("\n\n")
|
||||
.expect("I was expecting a double newline to split header and rest of code.");
|
||||
|
||||
let header_str = &code_str[0..blank_line_indx];
|
||||
let tail_str = &code_str[blank_line_indx..];
|
||||
|
||||
let mut scope = Scope::new(env.home, env.pool, env.var_store);
|
||||
let region = Region::new(0, 0, 0, 0);
|
||||
|
||||
let mut def_ids = Vec::<DefId>::new();
|
||||
|
||||
let def2_vec = str_to_def2(ast_arena, tail_str, env, &mut scope, region)?;
|
||||
|
||||
for def2 in def2_vec {
|
||||
let def_id = env.pool.add(def2);
|
||||
|
||||
def_ids.push(def_id);
|
||||
}
|
||||
|
||||
let ast_node_id = env.pool.add(Expr2::Blank);
|
||||
|
||||
Ok(AST {
|
||||
header: AppHeader::parse_from_string(header_str, ast_node_id),
|
||||
def_ids,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AppHeader {
|
||||
// TODO don't use mock struct and actually parse string
|
||||
pub fn parse_from_string(_header_str: &str, ast_node_id: ExprId) -> Self {
|
||||
AppHeader {
|
||||
app_name: "\"untitled-app\"".to_owned(),
|
||||
packages_base: "\"platform\"".to_owned(),
|
||||
imports: vec![],
|
||||
provides: vec!["main".to_owned()],
|
||||
ast_node_id,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#![allow(clippy::all)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
use crate::editor::ed_error::{EdResult, UnexpectedPattern2Variant};
|
||||
use crate::lang::ast::{ExprId, FloatVal, IntVal};
|
||||
use crate::lang::expr::{to_expr_id, Env, Output};
|
||||
use crate::lang::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
|
||||
|
@ -9,7 +10,7 @@ use bumpalo::collections::Vec as BumpVec;
|
|||
use roc_can::expr::unescape_char;
|
||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||
use roc_collections::all::BumpMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_parse::pattern::PatternType;
|
||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||
|
@ -482,6 +483,17 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
|||
symbols
|
||||
}
|
||||
|
||||
pub fn get_identifier_string(pattern: &Pattern2, interns: &Interns) -> EdResult<String> {
|
||||
match pattern {
|
||||
Pattern2::Identifier(symbol) => Ok(symbol.ident_str(interns).to_string()),
|
||||
other => UnexpectedPattern2Variant {
|
||||
required_pattern2: "Identifier".to_string(),
|
||||
encountered_pattern2: format!("{:?}", other),
|
||||
}
|
||||
.fail()?,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbols_and_variables_from_pattern(
|
||||
pool: &Pool,
|
||||
initial: &Pattern2,
|
||||
|
|
|
@ -17,6 +17,6 @@ mod window;
|
|||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn launch(filepaths: &[&Path]) -> io::Result<()> {
|
||||
editor::main::launch(filepaths)
|
||||
pub fn launch(project_dir_path_opt: Option<&Path>) -> io::Result<()> {
|
||||
editor::main::launch(project_dir_path_opt)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,12 @@ pub struct CaretWSelect {
|
|||
pub selection_opt: Option<Selection>,
|
||||
}
|
||||
|
||||
pub enum CaretPos {
|
||||
Start,
|
||||
Exact(TextPos),
|
||||
End,
|
||||
}
|
||||
|
||||
fn mk_some_sel(start_pos: TextPos, end_pos: TextPos) -> UIResult<Option<Selection>> {
|
||||
if start_pos == end_pos {
|
||||
Ok(None)
|
||||
|
@ -146,7 +152,7 @@ pub mod test_caret_w_select {
|
|||
|
||||
// Retrieve selection and position from formatted string
|
||||
pub fn convert_dsl_to_selection(lines: &[String]) -> Result<CaretWSelect, String> {
|
||||
let lines_str: String = lines.join("");
|
||||
let lines_str: String = lines.join("\n");
|
||||
|
||||
let parsed = LineParser::parse(Rule::linesWithSelect, &lines_str)
|
||||
.expect("Selection test DSL parsing failed");
|
||||
|
|
|
@ -13,14 +13,12 @@ use crate::ui::text::{
|
|||
use crate::ui::ui_error::UIResult;
|
||||
use crate::ui::util::is_newline;
|
||||
use crate::window::keyboard_input::Modifiers;
|
||||
use bumpalo::collections::String as BumpString;
|
||||
use bumpalo::Bump;
|
||||
use std::cmp::max;
|
||||
use std::cmp::min;
|
||||
use winit::event::VirtualKeyCode;
|
||||
|
||||
pub trait Lines {
|
||||
fn get_line(&self, line_nr: usize) -> UIResult<&str>;
|
||||
fn get_line_ref(&self, line_nr: usize) -> UIResult<&str>;
|
||||
|
||||
fn line_len(&self, line_nr: usize) -> UIResult<usize>;
|
||||
|
||||
|
@ -28,7 +26,7 @@ pub trait Lines {
|
|||
|
||||
fn nr_of_chars(&self) -> usize;
|
||||
|
||||
fn all_lines<'a>(&self, arena: &'a Bump) -> BumpString<'a>;
|
||||
fn all_lines_as_string(&self) -> String;
|
||||
|
||||
fn is_last_line(&self, line_nr: usize) -> bool;
|
||||
|
||||
|
@ -83,7 +81,7 @@ pub trait MutSelectableLines {
|
|||
|
||||
fn insert_str(&mut self, new_str: &str) -> UIResult<()>;
|
||||
|
||||
fn pop_char(&mut self) -> UIResult<()>;
|
||||
fn backspace(&mut self) -> UIResult<()>;
|
||||
|
||||
fn del_selection(&mut self) -> UIResult<()>;
|
||||
}
|
||||
|
@ -114,7 +112,7 @@ pub fn move_caret_left<T: Lines>(
|
|||
} else {
|
||||
let curr_line_len = lines.line_len(old_line_nr - 1)?;
|
||||
|
||||
(old_line_nr - 1, curr_line_len - 1)
|
||||
(old_line_nr - 1, curr_line_len)
|
||||
}
|
||||
} else {
|
||||
(old_line_nr, old_col_nr - 1)
|
||||
|
@ -185,7 +183,7 @@ pub fn move_caret_right<T: Lines>(
|
|||
let is_last_line = lines.is_last_line(old_line_nr);
|
||||
|
||||
if !is_last_line {
|
||||
if old_col_nr + 1 > curr_line_len - 1 {
|
||||
if old_col_nr + 1 > curr_line_len {
|
||||
(old_line_nr + 1, 0)
|
||||
} else {
|
||||
(old_line_nr, old_col_nr + 1)
|
||||
|
@ -263,7 +261,9 @@ pub fn move_caret_up<T: Lines>(
|
|||
let prev_line_len = lines.line_len(old_line_nr - 1)?;
|
||||
|
||||
if prev_line_len <= old_col_nr {
|
||||
(old_line_nr - 1, prev_line_len - 1)
|
||||
let new_column = if prev_line_len > 0 { prev_line_len } else { 0 };
|
||||
|
||||
(old_line_nr - 1, new_column)
|
||||
} else {
|
||||
(old_line_nr - 1, old_col_nr)
|
||||
}
|
||||
|
@ -331,7 +331,9 @@ pub fn move_caret_down<T: Lines>(
|
|||
|
||||
if next_line_len <= old_col_nr {
|
||||
if !is_last_line {
|
||||
(old_line_nr + 1, next_line_len - 1)
|
||||
let new_column = if next_line_len > 0 { next_line_len } else { 0 };
|
||||
|
||||
(old_line_nr + 1, new_column)
|
||||
} else {
|
||||
(old_line_nr + 1, next_line_len)
|
||||
}
|
||||
|
@ -382,7 +384,7 @@ pub fn move_caret_home<T: Lines>(
|
|||
let curr_line_nr = caret_w_select.caret_pos.line;
|
||||
let old_col_nr = caret_w_select.caret_pos.column;
|
||||
|
||||
let curr_line_str = lines.get_line(curr_line_nr)?;
|
||||
let curr_line_str = lines.get_line_ref(curr_line_nr)?;
|
||||
let line_char_iter = curr_line_str.chars();
|
||||
|
||||
let mut first_no_space_char_col = 0;
|
||||
|
|
|
@ -2,4 +2,5 @@ pub mod big_text_area;
|
|||
pub mod caret_w_select;
|
||||
pub mod lines;
|
||||
pub mod selection;
|
||||
mod text_buffer;
|
||||
pub mod text_pos;
|
||||
|
|
193
editor/src/ui/text/text_buffer.rs
Normal file
193
editor/src/ui/text/text_buffer.rs
Normal file
|
@ -0,0 +1,193 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::ui::{
|
||||
ui_error::{OutOfBounds, TextBufReadFailed, UIResult},
|
||||
util::{path_to_string, reader_from_path},
|
||||
};
|
||||
use snafu::ensure;
|
||||
|
||||
use super::{selection::Selection, text_pos::TextPos};
|
||||
use std::io::BufRead;
|
||||
|
||||
// Do not use for large amounts of text.
|
||||
// This should become a trait in the future and be implemented by a SmallTextBuffer and Rope(for large amounts of text)
|
||||
#[derive(Debug)]
|
||||
pub struct TextBuffer {
|
||||
pub lines: Vec<String>,
|
||||
}
|
||||
|
||||
impl TextBuffer {
|
||||
pub fn from_path(path: &Path) -> UIResult<Self> {
|
||||
let buf_reader = reader_from_path(path)?;
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
|
||||
for line in buf_reader.lines() {
|
||||
match line {
|
||||
Ok(line_str) => lines.push(line_str),
|
||||
Err(e) => {
|
||||
TextBufReadFailed {
|
||||
path_str: path_to_string(path),
|
||||
err_msg: e.to_string(),
|
||||
}
|
||||
.fail()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TextBuffer { lines })
|
||||
}
|
||||
|
||||
pub fn nr_of_chars(&self) -> usize {
|
||||
let mut nr_of_chars = 0;
|
||||
|
||||
for line in self.lines.iter() {
|
||||
nr_of_chars += line.len();
|
||||
}
|
||||
|
||||
nr_of_chars
|
||||
}
|
||||
|
||||
pub fn nr_of_lines(&self) -> usize {
|
||||
self.lines.len()
|
||||
}
|
||||
|
||||
pub fn get_line_ref(&self, line_nr: usize) -> UIResult<&str> {
|
||||
self.ensure_bounds(line_nr)?;
|
||||
// safe unwrap because we checked the length
|
||||
Ok(self.lines.get(line_nr).unwrap())
|
||||
}
|
||||
|
||||
pub fn line_len(&self, line_nr: usize) -> UIResult<usize> {
|
||||
Ok(self.get_line_ref(line_nr)?.len())
|
||||
}
|
||||
|
||||
fn ensure_bounds(&self, line_nr: usize) -> UIResult<()> {
|
||||
ensure!(
|
||||
line_nr < self.nr_of_lines(),
|
||||
OutOfBounds {
|
||||
index: line_nr,
|
||||
collection_name: "TextBuffer",
|
||||
len: self.nr_of_lines(),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_bounds_txt_pos(&self, txt_pos: TextPos) -> UIResult<()> {
|
||||
ensure!(
|
||||
txt_pos.line < self.nr_of_lines(),
|
||||
OutOfBounds {
|
||||
index: txt_pos.line,
|
||||
collection_name: "TextBuffer",
|
||||
len: self.nr_of_lines(),
|
||||
}
|
||||
);
|
||||
|
||||
let line_ref = self.get_line_ref(txt_pos.line)?;
|
||||
let line_len = line_ref.len();
|
||||
|
||||
ensure!(
|
||||
txt_pos.column <= line_len,
|
||||
OutOfBounds {
|
||||
index: txt_pos.column,
|
||||
collection_name: format!("Line in TextBuffer: {}", line_ref),
|
||||
len: line_len,
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn all_lines_ref(&self) -> &[String] {
|
||||
&self.lines
|
||||
}
|
||||
|
||||
pub fn get_selected_str(&self, selection: Selection) -> UIResult<String> {
|
||||
let start_line_nr = selection.start_pos.line;
|
||||
let start_col_nr = selection.start_pos.column;
|
||||
|
||||
let end_line_nr = selection.end_pos.line;
|
||||
let end_col_nr = selection.end_pos.column;
|
||||
|
||||
let mut selected_str = String::new();
|
||||
|
||||
if end_line_nr > start_line_nr {
|
||||
selected_str.push_str(&self.get_line_ref(start_line_nr)?[start_col_nr..]);
|
||||
|
||||
for line_nr in start_line_nr + 1..end_line_nr - 1 {
|
||||
selected_str.push_str(self.get_line_ref(line_nr)?);
|
||||
}
|
||||
|
||||
selected_str.push_str(&self.get_line_ref(end_line_nr)?[..end_col_nr]);
|
||||
} else {
|
||||
// start_line_nr == end_line_nr
|
||||
selected_str.push_str(&self.get_line_ref(start_line_nr)?[start_col_nr..end_col_nr]);
|
||||
}
|
||||
|
||||
Ok(selected_str)
|
||||
}
|
||||
|
||||
pub fn insert_str(&mut self, txt_pos: TextPos, new_str: &str) -> UIResult<()> {
|
||||
self.ensure_bounds_txt_pos(txt_pos)?;
|
||||
|
||||
// safe unwrap because we checked the length
|
||||
self.lines
|
||||
.get_mut(txt_pos.line)
|
||||
.unwrap()
|
||||
.insert_str(txt_pos.column, new_str);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn backspace_char(&mut self, txt_pos: TextPos) -> UIResult<()> {
|
||||
if txt_pos.column > 0 {
|
||||
let prev_col_pos = TextPos {
|
||||
line: txt_pos.line,
|
||||
column: txt_pos.column - 1,
|
||||
};
|
||||
|
||||
self.ensure_bounds_txt_pos(prev_col_pos)?;
|
||||
|
||||
let line_ref = self.lines.get_mut(prev_col_pos.line).unwrap(); // safe because of earlier bounds check
|
||||
|
||||
line_ref.remove(prev_col_pos.column);
|
||||
} else if txt_pos.line > 0 {
|
||||
self.lines.remove(txt_pos.line);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_selection(&mut self, selection: Selection) -> UIResult<()> {
|
||||
self.ensure_bounds_txt_pos(selection.start_pos)?;
|
||||
self.ensure_bounds_txt_pos(selection.end_pos)?;
|
||||
|
||||
let start_line_nr = selection.start_pos.line;
|
||||
let start_col_nr = selection.start_pos.column;
|
||||
let end_line_nr = selection.end_pos.line;
|
||||
let end_col_nr = selection.end_pos.column;
|
||||
|
||||
if end_line_nr > start_line_nr {
|
||||
// remove in reverse to prevent shifting indices
|
||||
if end_col_nr == self.line_len(end_line_nr)? {
|
||||
self.lines.remove(end_line_nr);
|
||||
} else {
|
||||
let line_ref = self.lines.get_mut(end_line_nr).unwrap(); // safe because of earlier bounds check
|
||||
line_ref.replace_range(..end_col_nr, "");
|
||||
}
|
||||
|
||||
self.lines.drain(start_line_nr + 1..end_line_nr);
|
||||
|
||||
let line_ref = self.lines.get_mut(start_line_nr).unwrap(); // safe because of earlier bounds check
|
||||
line_ref.replace_range(start_col_nr.., "")
|
||||
} else {
|
||||
// selection.end_pos.line == selection.start_pos.line
|
||||
let line_ref = self.lines.get_mut(selection.start_pos.line).unwrap(); // safe because of earlier bounds check
|
||||
|
||||
line_ref.replace_range(selection.start_pos.column..selection.end_pos.column, "")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use std::io;
|
||||
|
||||
use snafu::{Backtrace, Snafu};
|
||||
|
||||
//import errors as follows:
|
||||
|
@ -8,6 +10,16 @@ use snafu::{Backtrace, Snafu};
|
|||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum UIError {
|
||||
#[snafu(display(
|
||||
"LineInsertionFailed: line_nr ({}) needs to be <= nr_of_lines ({}).",
|
||||
line_nr,
|
||||
nr_of_lines
|
||||
))]
|
||||
LineInsertionFailed {
|
||||
line_nr: usize,
|
||||
nr_of_lines: usize,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
#[snafu(display(
|
||||
"OutOfBounds: index {} was out of bounds for {} with length {}.",
|
||||
index,
|
||||
|
@ -34,6 +46,13 @@ pub enum UIError {
|
|||
))]
|
||||
FileOpenFailed { path_str: String, err_msg: String },
|
||||
|
||||
#[snafu(display(
|
||||
"FileWriteFailed: failed to write to file with path {}, I got this IO error: {}.",
|
||||
path_str,
|
||||
source
|
||||
))]
|
||||
FileWriteFailed { source: io::Error, path_str: String },
|
||||
|
||||
#[snafu(display("TextBufReadFailed: the file {} could be opened but we encountered the following error while trying to read it: {}.", path_str, err_msg))]
|
||||
TextBufReadFailed { path_str: String, err_msg: String },
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::ui_error::{OutOfBounds, UIResult};
|
||||
use snafu::OptionExt;
|
||||
use std::slice::SliceIndex;
|
||||
use super::ui_error::{FileOpenFailed, FileWriteFailed, OutOfBounds, UIResult};
|
||||
use snafu::{OptionExt, ResultExt};
|
||||
use std::{fs::File, io::BufReader, path::Path, slice::SliceIndex};
|
||||
|
||||
pub fn is_newline(char_ref: &char) -> bool {
|
||||
let newline_codes = vec!['\u{d}', '\n'];
|
||||
|
@ -33,3 +33,27 @@ pub fn slice_get_mut<T>(
|
|||
|
||||
Ok(elt_ref)
|
||||
}
|
||||
|
||||
pub fn reader_from_path(path: &Path) -> UIResult<BufReader<File>> {
|
||||
match File::open(path) {
|
||||
Ok(file) => Ok(BufReader::new(file)),
|
||||
Err(e) => FileOpenFailed {
|
||||
path_str: path_to_string(path),
|
||||
err_msg: e.to_string(),
|
||||
}
|
||||
.fail()?,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_to_string(path: &Path) -> String {
|
||||
let mut path_str = String::new();
|
||||
path_str.push_str(&path.to_string_lossy());
|
||||
|
||||
path_str
|
||||
}
|
||||
|
||||
pub fn write_to_file(path: &Path, content: &str) -> UIResult<()> {
|
||||
std::fs::write(path, content).with_context(|| FileWriteFailed {
|
||||
path_str: path_to_string(path),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -27,6 +27,17 @@ impl Modifiers {
|
|||
|
||||
active
|
||||
}
|
||||
|
||||
// returns true if modifiers are active that can be active when the user wants to insert a new char; e.g.: shift+a to make A
|
||||
pub fn new_char_modifiers(&self) -> bool {
|
||||
self.no_modifiers()
|
||||
|| (self.shift && !self.ctrl && !self.alt && !self.logo) // e.g.: shift+a to make A
|
||||
|| (self.cmd_or_ctrl() && self.alt) // e.g.: ctrl+alt+2 to make @ on azerty keyboard
|
||||
}
|
||||
|
||||
fn no_modifiers(&self) -> bool {
|
||||
!self.shift && !self.ctrl && !self.alt && !self.logo
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no_mods() -> Modifiers {
|
||||
|
|
5
editor/tests/README.md
Normal file
5
editor/tests/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
# Where are the tests?
|
||||
|
||||
We have a lot of tests at the end of source files, this allows us to test functions that are not exposed by the editor itself.
|
||||
`editor/mvc/ed_update.rs` and `editor/ui/text/big_text_area.rs` have many important tests.
|
|
@ -1,4 +1,4 @@
|
|||
text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n" | "{" | "}" | "," | "." | "[" | "]" | ":" | "<" | ">" | "-" | "\"" )* }
|
||||
text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n" | "{" | "}" | "," | "." | "[" | "]" | ":" | "<" | ">" | "-" | "\"" | "=" )* }
|
||||
|
||||
caret = {"┃"}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue