mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +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/50000_lines.roc
|
||||||
editor/benches/resources/500_lines.roc
|
editor/benches/resources/500_lines.roc
|
||||||
|
|
||||||
|
# file editor creates when no arg is passed
|
||||||
|
new-roc-project
|
||||||
|
|
||||||
# rust cache (sccache folder)
|
# rust cache (sccache folder)
|
||||||
sccache_dir
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -3567,6 +3573,7 @@ dependencies = [
|
||||||
"confy",
|
"confy",
|
||||||
"copypasta",
|
"copypasta",
|
||||||
"env_logger 0.8.4",
|
"env_logger 0.8.4",
|
||||||
|
"fs_extra",
|
||||||
"futures",
|
"futures",
|
||||||
"glyph_brush",
|
"glyph_brush",
|
||||||
"im 15.0.0",
|
"im 15.0.0",
|
||||||
|
@ -3584,9 +3591,11 @@ dependencies = [
|
||||||
"quickcheck 1.0.3",
|
"quickcheck 1.0.3",
|
||||||
"quickcheck_macros 1.0.0",
|
"quickcheck_macros 1.0.0",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_fmt",
|
"roc_fmt",
|
||||||
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
|
@ -3598,6 +3607,8 @@ dependencies = [
|
||||||
"ropey",
|
"ropey",
|
||||||
"serde",
|
"serde",
|
||||||
"snafu",
|
"snafu",
|
||||||
|
"tempfile",
|
||||||
|
"uuid",
|
||||||
"ven_graph",
|
"ven_graph",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"wgpu_glyph",
|
"wgpu_glyph",
|
||||||
|
@ -4758,6 +4769,15 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
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]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|
|
@ -65,7 +65,7 @@ check-rustfmt:
|
||||||
RUN cargo fmt --all -- --check
|
RUN cargo fmt --all -- --check
|
||||||
|
|
||||||
check-typos:
|
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 ./
|
COPY --dir .github ci cli compiler docs editor examples linker nightly_benches packages roc_std www *.md LEGAL_DETAILS shell.nix ./
|
||||||
RUN typos
|
RUN typos
|
||||||
|
|
||||||
|
@ -97,7 +97,6 @@ test-all:
|
||||||
BUILD +test-zig
|
BUILD +test-zig
|
||||||
BUILD +check-rustfmt
|
BUILD +check-rustfmt
|
||||||
BUILD +check-clippy
|
BUILD +check-clippy
|
||||||
BUILD +check-typos
|
|
||||||
BUILD +test-rust
|
BUILD +test-rust
|
||||||
BUILD +verify-no-git-changes
|
BUILD +verify-no-git-changes
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn main() -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
launch_editor(&[])?;
|
launch_editor(None)?;
|
||||||
|
|
||||||
Ok(0)
|
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)
|
.subcommand_matches(CMD_EDIT)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.values_of_os(DIRECTORY_OR_FILES)
|
.values_of_os(DIRECTORY_OR_FILES)
|
||||||
|
.map(|mut values| values.next())
|
||||||
{
|
{
|
||||||
None => {
|
Some(Some(os_str)) => {
|
||||||
launch_editor(&[])?;
|
launch_editor(Some(Path::new(os_str)))?;
|
||||||
}
|
}
|
||||||
Some(values) => {
|
_ => {
|
||||||
let paths = values
|
launch_editor(None)?;
|
||||||
.map(|os_str| Path::new(os_str))
|
|
||||||
.collect::<Vec<&Path>>();
|
|
||||||
|
|
||||||
launch_editor(&paths)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,8 +184,8 @@ fn roc_files_recursive<P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "editor")]
|
#[cfg(feature = "editor")]
|
||||||
fn launch_editor(filepaths: &[&Path]) -> io::Result<()> {
|
fn launch_editor(project_dir_path: Option<&Path>) -> io::Result<()> {
|
||||||
roc_editor::launch(filepaths)
|
roc_editor::launch(project_dir_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "editor"))]
|
#[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!(work.is_empty());
|
||||||
debug_assert!(state.dependencies.solved_all());
|
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
|
/// (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
|
/// names cannot begin with a number, this has no chance of colliding
|
||||||
/// with actual user-defined variables.
|
/// with actual user-defined variables.
|
||||||
|
|
|
@ -32,7 +32,7 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, SyntaxError<'a>> {
|
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;
|
let min_indent = 0;
|
||||||
skip_second!(
|
skip_second!(
|
||||||
specialize(
|
specialize(
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
|
use crate::module::module_defs;
|
||||||
// use crate::module::module_defs;
|
// use crate::module::module_defs;
|
||||||
|
use crate::parser::Parser;
|
||||||
use crate::parser::{State, SyntaxError};
|
use crate::parser::{State, SyntaxError};
|
||||||
|
use bumpalo::collections::Vec as BumpVec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
|
|
||||||
|
@ -23,3 +26,15 @@ pub fn parse_loc_with<'a>(
|
||||||
Err(fail) => Err(SyntaxError::Expr(fail)),
|
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]
|
[dependencies]
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
|
roc_load = { path = "../compiler/load" }
|
||||||
|
roc_builtins = { path = "../compiler/builtins" }
|
||||||
roc_can = { path = "../compiler/can" }
|
roc_can = { path = "../compiler/can" }
|
||||||
roc_parse = { path = "../compiler/parse" }
|
roc_parse = { path = "../compiler/parse" }
|
||||||
roc_region = { path = "../compiler/region" }
|
roc_region = { path = "../compiler/region" }
|
||||||
|
@ -49,6 +51,9 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
nonempty = "0.6.0"
|
nonempty = "0.6.0"
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
fs_extra = "1.2.0"
|
||||||
|
|
||||||
[dependencies.bytemuck]
|
[dependencies.bytemuck]
|
||||||
version = "1.4"
|
version = "1.4"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::ui::text::lines::Lines;
|
use crate::ui::text::lines::Lines;
|
||||||
use crate::ui::text::selection::Selection;
|
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;
|
||||||
use crate::ui::util::slice_get_mut;
|
use crate::ui::util::slice_get_mut;
|
||||||
use bumpalo::collections::String as BumpString;
|
use std::cmp::Ordering;
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -14,31 +14,102 @@ pub struct CodeLines {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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(
|
pub fn insert_between_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
line_nr: usize,
|
line_nr: usize,
|
||||||
index: usize,
|
index: usize,
|
||||||
new_str: &str,
|
new_str: &str,
|
||||||
) -> UIResult<()> {
|
) -> UIResult<()> {
|
||||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
let nr_of_lines = self.lines.len();
|
||||||
|
|
||||||
line_ref.insert_str(index, new_str);
|
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();
|
self.nr_of_chars += new_str.len();
|
||||||
|
|
||||||
Ok(())
|
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<()> {
|
pub fn del_at_line(&mut self, line_nr: usize, index: usize) -> UIResult<()> {
|
||||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
||||||
|
|
||||||
|
@ -49,6 +120,18 @@ impl CodeLines {
|
||||||
Ok(())
|
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<()> {
|
pub fn del_selection(&mut self, selection: Selection) -> UIResult<()> {
|
||||||
if selection.is_on_same_line() {
|
if selection.is_on_same_line() {
|
||||||
let line_ref = slice_get_mut(selection.start_pos.line, &mut self.lines)?;
|
let line_ref = slice_get_mut(selection.start_pos.line, &mut self.lines)?;
|
||||||
|
@ -60,17 +143,36 @@ impl CodeLines {
|
||||||
|
|
||||||
Ok(())
|
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 {
|
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)?;
|
let line_string = slice_get(line_nr, &self.lines)?;
|
||||||
|
|
||||||
Ok(line_string)
|
Ok(line_string)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_len(&self, line_nr: usize) -> UIResult<usize> {
|
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 {
|
fn nr_of_lines(&self) -> usize {
|
||||||
|
@ -81,14 +183,8 @@ impl Lines for CodeLines {
|
||||||
self.nr_of_chars
|
self.nr_of_chars
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_lines<'a>(&self, arena: &'a Bump) -> BumpString<'a> {
|
fn all_lines_as_string(&self) -> String {
|
||||||
let mut lines = BumpString::with_capacity_in(self.nr_of_chars(), arena);
|
self.lines.join("\n")
|
||||||
|
|
||||||
for line in &self.lines {
|
|
||||||
lines.push_str(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_last_line(&self, line_nr: usize) -> bool {
|
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>> {
|
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 {
|
for row in &self.lines {
|
||||||
let row_str = row
|
let row_str = row
|
||||||
.chars()
|
.chars()
|
||||||
.map(|code_char| format!("'{}'", code_char))
|
.map(|code_char| format!("{}", code_char))
|
||||||
.collect::<Vec<String>>()
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::editor::theme::EdTheme;
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub code_font_size: f32,
|
pub code_font_size: f32,
|
||||||
|
pub debug_font_size: f32,
|
||||||
pub ed_theme: EdTheme,
|
pub ed_theme: EdTheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
code_font_size: 30.0,
|
code_font_size: 30.0,
|
||||||
|
debug_font_size: 20.0,
|
||||||
ed_theme: EdTheme::default(),
|
ed_theme: EdTheme::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::lang::parse::ASTNodeId;
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
use crate::{editor::slow_pool::MarkNodeId, ui::text::text_pos::TextPos};
|
use crate::{editor::slow_pool::MarkNodeId, ui::text::text_pos::TextPos};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
@ -11,6 +12,24 @@ use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu};
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
#[snafu(visibility(pub))]
|
#[snafu(visibility(pub))]
|
||||||
pub enum EdError {
|
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(
|
#[snafu(display(
|
||||||
"CaretNotFound: No carets were found in the expected node with id {}",
|
"CaretNotFound: No carets were found in the expected node with id {}",
|
||||||
node_id
|
node_id
|
||||||
|
@ -43,6 +62,17 @@ pub enum EdError {
|
||||||
backtrace: Backtrace,
|
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."))]
|
#[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))]
|
||||||
GetContentOnNestedNode { backtrace: Backtrace },
|
GetContentOnNestedNode { backtrace: Backtrace },
|
||||||
|
|
||||||
|
@ -99,6 +129,12 @@ pub enum EdError {
|
||||||
backtrace: Backtrace,
|
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."))]
|
#[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 },
|
NodeWithoutAttributes { backtrace: Backtrace },
|
||||||
|
|
||||||
|
@ -131,6 +167,17 @@ pub enum EdError {
|
||||||
backtrace: Backtrace,
|
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(
|
#[snafu(display(
|
||||||
"UnexpectedEmptyPoolVec: expected PoolVec {} to have at least one element.",
|
"UnexpectedEmptyPoolVec: expected PoolVec {} to have at least one element.",
|
||||||
descriptive_vec_name
|
descriptive_vec_name
|
||||||
|
@ -154,7 +201,10 @@ pub enum EdError {
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("ParseError: Failed to parse AST: SyntaxError: {}.", syntax_err))]
|
#[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."))]
|
#[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmptyRecord."))]
|
||||||
RecordWithoutFields { backtrace: Backtrace },
|
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 {
|
fn color_backtrace(backtrace: &snafu::Backtrace) -> String {
|
||||||
let backtrace_str = format!("{}", backtrace);
|
let backtrace_str = format!("{}", backtrace);
|
||||||
let backtrace_split = backtrace_str.split('\n');
|
let backtrace_split = backtrace_str.split('\n');
|
||||||
|
|
|
@ -1,40 +1,28 @@
|
||||||
use crate::editor::ed_error::EdResult;
|
use crate::editor::ed_error::EdResult;
|
||||||
use crate::editor::ed_error::NestedNodeWithoutChildren;
|
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::mvc::ed_model::EdModel;
|
||||||
use crate::editor::slow_pool::MarkNodeId;
|
use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::util::first_last_index_of;
|
use crate::editor::util::first_last_index_of;
|
||||||
use crate::editor::util::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::selection::Selection;
|
||||||
use crate::ui::text::text_pos::TextPos;
|
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 crate::ui::util::{slice_get, slice_get_mut};
|
||||||
use snafu::OptionExt;
|
use snafu::OptionExt;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::markup::nodes::get_root_mark_node_id;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GridNodeMap {
|
pub struct GridNodeMap {
|
||||||
pub lines: Vec<Vec<MarkNodeId>>,
|
pub lines: Vec<Vec<MarkNodeId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridNodeMap {
|
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(
|
pub fn insert_between_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
line_nr: usize,
|
line_nr: usize,
|
||||||
|
@ -42,18 +30,105 @@ impl GridNodeMap {
|
||||||
len: usize,
|
len: usize,
|
||||||
node_id: MarkNodeId,
|
node_id: MarkNodeId,
|
||||||
) -> UIResult<()> {
|
) -> UIResult<()> {
|
||||||
let line_ref = slice_get_mut(line_nr, &mut self.lines)?;
|
let nr_of_lines = self.lines.len();
|
||||||
let new_cols_vec: Vec<MarkNodeId> = std::iter::repeat(node_id).take(len).collect();
|
|
||||||
|
|
||||||
line_ref.splice(index..index, new_cols_vec);
|
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(())
|
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)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -64,16 +139,12 @@ impl GridNodeMap {
|
||||||
|
|
||||||
line_ref.drain(selection.start_pos.column..selection.end_pos.column);
|
line_ref.drain(selection.start_pos.column..selection.end_pos.column);
|
||||||
} else {
|
} else {
|
||||||
// TODO support multiline
|
unimplemented!("TODO support deleting multiline selection")
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*pub fn new_line(&mut self) {
|
|
||||||
self.lines.push(vec![])
|
|
||||||
}*/
|
|
||||||
|
|
||||||
pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult<MarkNodeId> {
|
pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult<MarkNodeId> {
|
||||||
let line = slice_get(caret_pos.line, &self.lines)?;
|
let line = slice_get(caret_pos.line, &self.lines)?;
|
||||||
let node_id = slice_get(caret_pos.column, line)?;
|
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
|
// returns start and end pos of Expr2/Def2, relevant AST node and MarkNodeId of the corresponding MarkupNode
|
||||||
pub fn get_expr_start_end_pos(
|
pub fn get_block_start_end_pos(
|
||||||
&self,
|
&self,
|
||||||
caret_pos: TextPos,
|
caret_pos: TextPos,
|
||||||
ed_model: &EdModel,
|
ed_model: &EdModel,
|
||||||
) -> EdResult<(TextPos, TextPos, ExprId, MarkNodeId)> {
|
) -> EdResult<(TextPos, TextPos, ASTNodeId, MarkNodeId)> {
|
||||||
let line = slice_get(caret_pos.line, &self.lines)?;
|
let line = slice_get(caret_pos.line, &self.lines)?;
|
||||||
let node_id = slice_get(caret_pos.column, line)?;
|
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() {
|
if node.is_nested() {
|
||||||
let (start_pos, end_pos) = self.get_nested_start_end_pos(*node_id, ed_model)?;
|
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 (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_node_id = slice_get(first_node_index, line)?;
|
||||||
let curr_ast_node_id = ed_model
|
let curr_ast_node_id = ed_model.mark_node_pool.get(*curr_node_id).get_ast_node_id();
|
||||||
.markup_node_pool
|
|
||||||
.get(*curr_node_id)
|
|
||||||
.get_ast_node_id();
|
|
||||||
|
|
||||||
let mut expr_start_index = first_node_index;
|
let mut expr_start_index = first_node_index;
|
||||||
let mut expr_end_index = last_node_index;
|
let mut expr_end_index = last_node_index;
|
||||||
|
@ -165,7 +233,7 @@ impl GridNodeMap {
|
||||||
for i in (0..first_node_index).rev() {
|
for i in (0..first_node_index).rev() {
|
||||||
let prev_pos_node_id = slice_get(i, line)?;
|
let prev_pos_node_id = slice_get(i, line)?;
|
||||||
let prev_ast_node_id = ed_model
|
let prev_ast_node_id = ed_model
|
||||||
.markup_node_pool
|
.mark_node_pool
|
||||||
.get(*prev_pos_node_id)
|
.get(*prev_pos_node_id)
|
||||||
.get_ast_node_id();
|
.get_ast_node_id();
|
||||||
|
|
||||||
|
@ -187,7 +255,7 @@ impl GridNodeMap {
|
||||||
for i in last_node_index..line.len() {
|
for i in last_node_index..line.len() {
|
||||||
let next_pos_node_id = slice_get(i, line)?;
|
let next_pos_node_id = slice_get(i, line)?;
|
||||||
let next_ast_node_id = ed_model
|
let next_ast_node_id = ed_model
|
||||||
.markup_node_pool
|
.mark_node_pool
|
||||||
.get(*next_pos_node_id)
|
.get(*next_pos_node_id)
|
||||||
.get_ast_node_id();
|
.get_ast_node_id();
|
||||||
|
|
||||||
|
@ -204,7 +272,7 @@ impl GridNodeMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
let correct_mark_node_id =
|
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((
|
Ok((
|
||||||
TextPos {
|
TextPos {
|
||||||
|
@ -225,12 +293,12 @@ impl GridNodeMap {
|
||||||
// `{` is not the entire Expr2
|
// `{` is not the entire Expr2
|
||||||
fn get_top_node_with_expr_id(
|
fn get_top_node_with_expr_id(
|
||||||
curr_node_id: MarkNodeId,
|
curr_node_id: MarkNodeId,
|
||||||
markup_node_pool: &SlowPool,
|
mark_node_pool: &SlowPool,
|
||||||
) -> MarkNodeId {
|
) -> 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() {
|
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() {
|
if parent.get_ast_node_id() == curr_node.get_ast_node_id() {
|
||||||
parent_id
|
parent_id
|
||||||
|
@ -247,30 +315,109 @@ impl GridNodeMap {
|
||||||
nested_node_id: MarkNodeId,
|
nested_node_id: MarkNodeId,
|
||||||
ed_model: &EdModel,
|
ed_model: &EdModel,
|
||||||
) -> EdResult<(TextPos, TextPos)> {
|
) -> 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 right_most_leaf = self.get_rightmost_leaf(nested_node_id, ed_model)?;
|
||||||
let first_child_id = all_child_ids
|
|
||||||
.first()
|
|
||||||
.with_context(|| NestedNodeWithoutChildren {
|
|
||||||
node_id: nested_node_id,
|
|
||||||
})?;
|
|
||||||
let last_child_id = all_child_ids
|
|
||||||
.last()
|
|
||||||
.with_context(|| NestedNodeWithoutChildren {
|
|
||||||
node_id: nested_node_id,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let expr_start_pos = ed_model
|
let expr_start_pos = ed_model
|
||||||
.grid_node_map
|
.grid_node_map
|
||||||
.get_node_position(*first_child_id, true)?;
|
.get_node_position(left_most_leaf, true)?;
|
||||||
let expr_end_pos = ed_model
|
let expr_end_pos = ed_model
|
||||||
.grid_node_map
|
.grid_node_map
|
||||||
.get_node_position(*last_child_id, false)?
|
.get_node_position(right_most_leaf, false)?
|
||||||
.increment_col();
|
.increment_col();
|
||||||
|
|
||||||
Ok((expr_start_pos, expr_end_pos))
|
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,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
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,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
children_ids = ed_model
|
||||||
|
.mark_node_pool
|
||||||
|
.get(last_child_id)
|
||||||
|
.get_children_ids();
|
||||||
|
}
|
||||||
|
|
||||||
|
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()],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GridNodeMap {
|
impl fmt::Display for GridNodeMap {
|
||||||
|
@ -282,10 +429,10 @@ impl fmt::Display for GridNodeMap {
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
write!(f, "{}", row_str)?;
|
writeln!(f, "{}", row_str)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, " (grid_node_map)")?;
|
writeln!(f, "(grid_node_map, {:?} lines)", self.lines.len())?;
|
||||||
|
|
||||||
Ok(())
|
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)?,
|
F11 => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use super::keyboard_input;
|
use super::keyboard_input;
|
||||||
use super::style::CODE_TXT_XY;
|
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;
|
||||||
use crate::editor::mvc::ed_view::RenderedWgpu;
|
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::{
|
use crate::editor::{
|
||||||
config::Config,
|
config::Config,
|
||||||
ed_error::print_err,
|
ed_error::print_err,
|
||||||
|
@ -20,17 +19,23 @@ use crate::graphics::{
|
||||||
};
|
};
|
||||||
use crate::lang::expr::Env;
|
use crate::lang::expr::Env;
|
||||||
use crate::lang::pool::Pool;
|
use crate::lang::pool::Pool;
|
||||||
use crate::ui::ui_error::UIError::FileOpenFailed;
|
use crate::ui::text::caret_w_select::CaretPos;
|
||||||
use crate::ui::util::slice_get;
|
use crate::ui::util::path_to_string;
|
||||||
use bumpalo::collections::String as BumpString;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use cgmath::Vector2;
|
use cgmath::Vector2;
|
||||||
|
use fs_extra::dir::{copy, ls, CopyOptions, DirEntryAttr, DirEntryValue};
|
||||||
use pipelines::RectResources;
|
use pipelines::RectResources;
|
||||||
use roc_module::symbol::Interns;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_module::symbol::{IdentIds, ModuleIds};
|
use roc_collections::all::MutMap;
|
||||||
|
use roc_load;
|
||||||
|
use roc_load::file::LoadedModule;
|
||||||
|
use roc_module::symbol::IdentIds;
|
||||||
use roc_types::subs::VarStore;
|
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 std::{error::Error, io, path::Path};
|
||||||
use wgpu::{CommandEncoder, RenderPass, TextureView};
|
use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView};
|
||||||
use wgpu_glyph::GlyphBrush;
|
use wgpu_glyph::GlyphBrush;
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
|
@ -48,26 +53,13 @@ use winit::{
|
||||||
|
|
||||||
/// The editor is actually launched from the CLI if you pass it zero arguments,
|
/// 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.
|
/// or if you provide it 1 or more files or directories to open on launch.
|
||||||
pub fn launch(filepaths: &[&Path]) -> io::Result<()> {
|
pub fn launch(project_dir_path_opt: Option<&Path>) -> io::Result<()> {
|
||||||
//TODO support using multiple filepaths
|
run_event_loop(project_dir_path_opt).expect("Error running event loop");
|
||||||
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");
|
|
||||||
|
|
||||||
Ok(())
|
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();
|
env_logger::init();
|
||||||
|
|
||||||
// Open window and create a surface
|
// 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 env_arena = Bump::new();
|
||||||
let code_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 mut var_store = VarStore::default();
|
||||||
let dep_idents = IdentIds::exposed_builtins(8);
|
let dep_idents = IdentIds::exposed_builtins(8);
|
||||||
|
|
||||||
let exposed_ident_ids = IdentIds::default();
|
let exposed_ident_ids = IdentIds::default();
|
||||||
let mut module_ids = ModuleIds::default();
|
let module_ids = loaded_module.interns.module_ids.clone();
|
||||||
let mod_id = module_ids.get_or_insert(&"ModId123".into());
|
|
||||||
|
|
||||||
let interns = Interns {
|
|
||||||
module_ids,
|
|
||||||
all_ident_ids: IdentIds::exposed_builtins(8),
|
|
||||||
};
|
|
||||||
|
|
||||||
let env = Env::new(
|
let env = Env::new(
|
||||||
mod_id,
|
loaded_module.module_id,
|
||||||
&env_arena,
|
&env_arena,
|
||||||
&mut env_pool,
|
&mut env_pool,
|
||||||
&mut var_store,
|
&mut var_store,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
&interns.module_ids,
|
&module_ids,
|
||||||
exposed_ident_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_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 {
|
match ed_model_res {
|
||||||
Ok(mut ed_model) => {
|
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),
|
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 {
|
if let Err(e) = input_outcome_res {
|
||||||
print_err(&e)
|
print_err(&e)
|
||||||
} else if let Ok(InputOutcome::Ignored) = input_outcome_res {
|
} 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 {
|
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();
|
let borrowed_text = text_section.to_borrowed();
|
||||||
|
|
||||||
glyph_brush.queue(borrowed_text);
|
glyph_brush.queue(borrowed_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_all_rects(
|
// draw first layer of text
|
||||||
&rendered_wgpu.rects,
|
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,
|
&mut encoder,
|
||||||
&frame.view,
|
&frame.view,
|
||||||
&gpu_device,
|
&gpu_device,
|
||||||
&rect_resources,
|
&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 {
|
} 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(
|
queue_no_file_text(
|
||||||
&size,
|
&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
|
glyph_brush
|
||||||
.draw_queued(
|
.draw_queued(
|
||||||
&gpu_device,
|
&gpu_device,
|
||||||
|
@ -351,7 +362,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||||
size.width,
|
size.width,
|
||||||
size.height,
|
size.height,
|
||||||
)
|
)
|
||||||
.expect("Draw queued");
|
.expect("Failed to draw queued text.");
|
||||||
|
|
||||||
staging_belt.finish();
|
staging_belt.finish();
|
||||||
cmd_queue.submit(Some(encoder.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_all_rects(
|
fn draw_rects(
|
||||||
all_rects: &[Rect],
|
all_rects: &[Rect],
|
||||||
encoder: &mut CommandEncoder,
|
encoder: &mut CommandEncoder,
|
||||||
texture_view: &TextureView,
|
texture_view: &TextureView,
|
||||||
gpu_device: &wgpu::Device,
|
gpu_device: &wgpu::Device,
|
||||||
rect_resources: &RectResources,
|
rect_resources: &RectResources,
|
||||||
ed_theme: &EdTheme,
|
load_op: LoadOp<wgpu::Color>,
|
||||||
) {
|
) {
|
||||||
let rect_buffers = create_rect_buffers(gpu_device, encoder, all_rects);
|
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_pipeline(&rect_resources.pipeline);
|
||||||
render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]);
|
render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]);
|
||||||
|
@ -399,16 +410,14 @@ fn draw_all_rects(
|
||||||
fn begin_render_pass<'a>(
|
fn begin_render_pass<'a>(
|
||||||
encoder: &'a mut CommandEncoder,
|
encoder: &'a mut CommandEncoder,
|
||||||
texture_view: &'a TextureView,
|
texture_view: &'a TextureView,
|
||||||
ed_theme: &EdTheme,
|
load_op: LoadOp<wgpu::Color>,
|
||||||
) -> RenderPass<'a> {
|
) -> RenderPass<'a> {
|
||||||
let bg_color = to_wgpu_color(ed_theme.background);
|
|
||||||
|
|
||||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||||
view: texture_view,
|
view: texture_view,
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations {
|
||||||
load: wgpu::LoadOp::Clear(bg_color),
|
load: load_op,
|
||||||
store: true,
|
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(
|
fn queue_no_file_text(
|
||||||
size: &PhysicalSize<u32>,
|
size: &PhysicalSize<u32>,
|
||||||
text: &str,
|
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 attribute;
|
||||||
|
pub mod common_nodes;
|
||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
|
|
|
@ -1,42 +1,62 @@
|
||||||
use super::attribute::Attributes;
|
use super::attribute::Attributes;
|
||||||
use crate::editor::ed_error::EdResult;
|
use crate::editor::ed_error::EdResult;
|
||||||
use crate::editor::ed_error::ExpectedTextNode;
|
use crate::editor::ed_error::ExpectedTextNode;
|
||||||
use crate::editor::ed_error::GetContentOnNestedNode;
|
|
||||||
use crate::editor::ed_error::{NestedNodeMissingChild, NestedNodeRequired};
|
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::MarkNodeId;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
use crate::lang::ast::Def2;
|
||||||
use crate::lang::{expr::Env, pool::PoolStr};
|
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 crate::ui::util::slice_get;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_module::symbol::Interns;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MarkupNode {
|
pub enum MarkupNode {
|
||||||
Nested {
|
Nested {
|
||||||
ast_node_id: ExprId,
|
ast_node_id: ASTNodeId,
|
||||||
children_ids: Vec<MarkNodeId>,
|
children_ids: Vec<MarkNodeId>,
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
|
newlines_at_end: usize,
|
||||||
},
|
},
|
||||||
Text {
|
Text {
|
||||||
content: String,
|
content: String,
|
||||||
ast_node_id: ExprId,
|
ast_node_id: ASTNodeId,
|
||||||
syn_high_style: HighlightStyle,
|
syn_high_style: HighlightStyle,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
|
newlines_at_end: usize,
|
||||||
},
|
},
|
||||||
Blank {
|
Blank {
|
||||||
ast_node_id: ExprId,
|
ast_node_id: ASTNodeId,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank
|
syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
|
newlines_at_end: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MarkupNode {
|
impl MarkupNode {
|
||||||
pub fn get_ast_node_id(&self) -> ExprId {
|
pub fn get_ast_node_id(&self) -> ASTNodeId {
|
||||||
match self {
|
match self {
|
||||||
MarkupNode::Nested { ast_node_id, .. } => *ast_node_id,
|
MarkupNode::Nested { ast_node_id, .. } => *ast_node_id,
|
||||||
MarkupNode::Text { 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() {
|
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()
|
parent_node.get_children_ids()
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +94,7 @@ impl MarkupNode {
|
||||||
pub fn get_child_indices(
|
pub fn get_child_indices(
|
||||||
&self,
|
&self,
|
||||||
child_id: MarkNodeId,
|
child_id: MarkNodeId,
|
||||||
markup_node_pool: &SlowPool,
|
mark_node_pool: &SlowPool,
|
||||||
) -> EdResult<(usize, usize)> {
|
) -> EdResult<(usize, usize)> {
|
||||||
match self {
|
match self {
|
||||||
MarkupNode::Nested { children_ids, .. } => {
|
MarkupNode::Nested { children_ids, .. } => {
|
||||||
|
@ -87,7 +107,7 @@ impl MarkupNode {
|
||||||
mark_child_index_opt = Some(indx);
|
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 ',', '[', ']'
|
// a node that points to the same ast_node as the parent is a ',', '[', ']'
|
||||||
// those are not "real" ast children
|
// those are not "real" ast children
|
||||||
if child_mark_node.get_ast_node_id() != self_ast_id {
|
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) -> String {
|
||||||
pub fn get_content(&self) -> EdResult<String> {
|
|
||||||
match self {
|
match self {
|
||||||
MarkupNode::Nested { .. } => GetContentOnNestedNode {}.fail(),
|
MarkupNode::Nested { .. } => "".to_owned(),
|
||||||
MarkupNode::Text { content, .. } => Ok(content.clone()),
|
MarkupNode::Text { content, .. } => content.clone(),
|
||||||
MarkupNode::Blank { .. } => Ok(BLANK_PLACEHOLDER.to_owned()),
|
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> {
|
pub fn get_content_mut(&mut self) -> EdResult<&mut String> {
|
||||||
match self {
|
match self {
|
||||||
MarkupNode::Nested { .. } => ExpectedTextNode {
|
MarkupNode::Nested { .. } => ExpectedTextNode {
|
||||||
|
@ -172,11 +202,10 @@ impl MarkupNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_all_alphanumeric(&self) -> EdResult<bool> {
|
pub fn is_all_alphanumeric(&self) -> bool {
|
||||||
Ok(self
|
self.get_content()
|
||||||
.get_content()?
|
|
||||||
.chars()
|
.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<()> {
|
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 {
|
pub fn is_nested(&self) -> bool {
|
||||||
matches!(self, MarkupNode::Nested { .. })
|
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 {
|
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 COLON: &str = ": ";
|
||||||
pub const COMMA: &str = ", ";
|
pub const COMMA: &str = ", ";
|
||||||
pub const STRING_QUOTES: &str = "\"\"";
|
pub const STRING_QUOTES: &str = "\"\"";
|
||||||
|
pub const EQUALS: &str = " = ";
|
||||||
|
|
||||||
fn new_markup_node(
|
fn new_markup_node(
|
||||||
text: String,
|
text: String,
|
||||||
node_id: ExprId,
|
node_id: ASTNodeId,
|
||||||
highlight_style: HighlightStyle,
|
highlight_style: HighlightStyle,
|
||||||
markup_node_pool: &mut SlowPool,
|
mark_node_pool: &mut SlowPool,
|
||||||
) -> MarkNodeId {
|
) -> MarkNodeId {
|
||||||
let node = MarkupNode::Text {
|
let node = MarkupNode::Text {
|
||||||
content: text,
|
content: text,
|
||||||
|
@ -236,9 +294,51 @@ fn new_markup_node(
|
||||||
syn_high_style: highlight_style,
|
syn_high_style: highlight_style,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt: None,
|
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
|
// make Markup Nodes: generate String representation, assign Highlighting Style
|
||||||
|
@ -247,55 +347,44 @@ pub fn expr2_to_markup<'a, 'b>(
|
||||||
env: &mut Env<'b>,
|
env: &mut Env<'b>,
|
||||||
expr2: &Expr2,
|
expr2: &Expr2,
|
||||||
expr2_node_id: ExprId,
|
expr2_node_id: ExprId,
|
||||||
markup_node_pool: &mut SlowPool,
|
mark_node_pool: &mut SlowPool,
|
||||||
) -> MarkNodeId {
|
interns: &Interns,
|
||||||
match expr2 {
|
) -> EdResult<MarkNodeId> {
|
||||||
|
let ast_node_id = ASTNodeId::AExprId(expr2_node_id);
|
||||||
|
|
||||||
|
let mark_node_id = match expr2 {
|
||||||
Expr2::SmallInt { text, .. }
|
Expr2::SmallInt { text, .. }
|
||||||
| Expr2::I128 { text, .. }
|
| Expr2::I128 { text, .. }
|
||||||
| Expr2::U128 { text, .. }
|
| Expr2::U128 { text, .. }
|
||||||
| Expr2::Float { text, .. } => {
|
| Expr2::Float { text, .. } => {
|
||||||
let num_str = get_string(env, text);
|
let num_str = get_string(env, text);
|
||||||
|
|
||||||
new_markup_node(
|
new_markup_node(num_str, ast_node_id, HighlightStyle::Number, mark_node_pool)
|
||||||
num_str,
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Number,
|
|
||||||
markup_node_pool,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Expr2::Str(text) => new_markup_node(
|
Expr2::Str(text) => new_markup_node(
|
||||||
"\"".to_owned() + text.as_str(env.pool) + "\"",
|
"\"".to_owned() + text.as_str(env.pool) + "\"",
|
||||||
expr2_node_id,
|
ast_node_id,
|
||||||
HighlightStyle::String,
|
HighlightStyle::String,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
),
|
),
|
||||||
Expr2::GlobalTag { name, .. } => new_markup_node(
|
Expr2::GlobalTag { name, .. } => new_markup_node(
|
||||||
get_string(env, name),
|
get_string(env, name),
|
||||||
expr2_node_id,
|
ast_node_id,
|
||||||
HighlightStyle::Type,
|
HighlightStyle::Type,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
),
|
),
|
||||||
Expr2::Call { expr: expr_id, .. } => {
|
Expr2::Call { expr: expr_id, .. } => {
|
||||||
let expr = env.pool.get(*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) => {
|
Expr2::Var(symbol) => {
|
||||||
//TODO make bump_format with arena
|
//TODO make bump_format with arena
|
||||||
let text = format!("{:?}", symbol);
|
let text = format!("{:?}", symbol);
|
||||||
new_markup_node(
|
new_markup_node(text, ast_node_id, HighlightStyle::Variable, mark_node_pool)
|
||||||
text,
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Variable,
|
|
||||||
markup_node_pool,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Expr2::List { elems, .. } => {
|
Expr2::List { elems, .. } => {
|
||||||
let mut children_ids = vec![new_markup_node(
|
let mut children_ids =
|
||||||
LEFT_SQUARE_BR.to_string(),
|
vec![mark_node_pool.add(new_left_square_mn(expr2_node_id, None))];
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
)];
|
|
||||||
|
|
||||||
let indexed_node_ids: Vec<(usize, ExprId)> =
|
let indexed_node_ids: Vec<(usize, ExprId)> =
|
||||||
elems.iter(env.pool).copied().enumerate().collect();
|
elems.iter(env.pool).copied().enumerate().collect();
|
||||||
|
@ -308,64 +397,43 @@ pub fn expr2_to_markup<'a, 'b>(
|
||||||
env,
|
env,
|
||||||
sub_expr2,
|
sub_expr2,
|
||||||
*node_id,
|
*node_id,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
));
|
interns,
|
||||||
|
)?);
|
||||||
|
|
||||||
if idx + 1 < elems.len() {
|
if idx + 1 < elems.len() {
|
||||||
children_ids.push(new_markup_node(
|
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||||
", ".to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Operator,
|
|
||||||
markup_node_pool,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
children_ids.push(new_markup_node(
|
children_ids.push(mark_node_pool.add(new_right_square_mn(expr2_node_id, None)));
|
||||||
RIGHT_SQUARE_BR.to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
));
|
|
||||||
|
|
||||||
let list_node = MarkupNode::Nested {
|
let list_node = MarkupNode::Nested {
|
||||||
ast_node_id: expr2_node_id,
|
ast_node_id,
|
||||||
children_ids,
|
children_ids,
|
||||||
parent_id_opt: None,
|
parent_id_opt: None,
|
||||||
|
newlines_at_end: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
markup_node_pool.add(list_node)
|
mark_node_pool.add(list_node)
|
||||||
}
|
}
|
||||||
Expr2::EmptyRecord => {
|
Expr2::EmptyRecord => {
|
||||||
let children_ids = vec![
|
let children_ids = vec![
|
||||||
new_markup_node(
|
mark_node_pool.add(new_left_accolade_mn(expr2_node_id, None)),
|
||||||
LEFT_ACCOLADE.to_string(),
|
mark_node_pool.add(new_right_accolade_mn(expr2_node_id, None)),
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
),
|
|
||||||
new_markup_node(
|
|
||||||
RIGHT_ACCOLADE.to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let record_node = MarkupNode::Nested {
|
let record_node = MarkupNode::Nested {
|
||||||
ast_node_id: expr2_node_id,
|
ast_node_id,
|
||||||
children_ids,
|
children_ids,
|
||||||
parent_id_opt: None,
|
parent_id_opt: None,
|
||||||
|
newlines_at_end: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
markup_node_pool.add(record_node)
|
mark_node_pool.add(record_node)
|
||||||
}
|
}
|
||||||
Expr2::Record { fields, .. } => {
|
Expr2::Record { fields, .. } => {
|
||||||
let mut children_ids = vec![new_markup_node(
|
let mut children_ids =
|
||||||
LEFT_ACCOLADE.to_string(),
|
vec![mark_node_pool.add(new_left_accolade_mn(expr2_node_id, None))];
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
)];
|
|
||||||
|
|
||||||
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
for (idx, field_node_id) in fields.iter_node_ids().enumerate() {
|
||||||
let record_field = env.pool.get(field_node_id);
|
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(
|
children_ids.push(new_markup_node(
|
||||||
field_name.as_str(env.pool).to_owned(),
|
field_name.as_str(env.pool).to_owned(),
|
||||||
expr2_node_id,
|
ast_node_id,
|
||||||
HighlightStyle::RecordField,
|
HighlightStyle::RecordField,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
));
|
));
|
||||||
|
|
||||||
match record_field {
|
match record_field {
|
||||||
RecordField::InvalidLabelOnly(_, _) => (),
|
RecordField::InvalidLabelOnly(_, _) => (),
|
||||||
RecordField::LabelOnly(_, _, _) => (),
|
RecordField::LabelOnly(_, _, _) => (),
|
||||||
RecordField::LabeledValue(_, _, sub_expr2_node_id) => {
|
RecordField::LabeledValue(_, _, sub_expr2_node_id) => {
|
||||||
children_ids.push(new_markup_node(
|
children_ids.push(mark_node_pool.add(new_colon_mn(expr2_node_id, None)));
|
||||||
COLON.to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Operator,
|
|
||||||
markup_node_pool,
|
|
||||||
));
|
|
||||||
|
|
||||||
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
let sub_expr2 = env.pool.get(*sub_expr2_node_id);
|
||||||
children_ids.push(expr2_to_markup(
|
children_ids.push(expr2_to_markup(
|
||||||
|
@ -396,66 +459,117 @@ pub fn expr2_to_markup<'a, 'b>(
|
||||||
env,
|
env,
|
||||||
sub_expr2,
|
sub_expr2,
|
||||||
*sub_expr2_node_id,
|
*sub_expr2_node_id,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
));
|
interns,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx + 1 < fields.len() {
|
if idx + 1 < fields.len() {
|
||||||
children_ids.push(new_markup_node(
|
children_ids.push(mark_node_pool.add(new_comma_mn(expr2_node_id, None)));
|
||||||
", ".to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Operator,
|
|
||||||
markup_node_pool,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
children_ids.push(new_markup_node(
|
children_ids.push(mark_node_pool.add(new_right_accolade_mn(expr2_node_id, None)));
|
||||||
RIGHT_ACCOLADE.to_string(),
|
|
||||||
expr2_node_id,
|
|
||||||
HighlightStyle::Bracket,
|
|
||||||
markup_node_pool,
|
|
||||||
));
|
|
||||||
|
|
||||||
let record_node = MarkupNode::Nested {
|
let record_node = MarkupNode::Nested {
|
||||||
ast_node_id: expr2_node_id,
|
ast_node_id,
|
||||||
children_ids,
|
children_ids,
|
||||||
parent_id_opt: None,
|
parent_id_opt: None,
|
||||||
|
newlines_at_end: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
markup_node_pool.add(record_node)
|
mark_node_pool.add(record_node)
|
||||||
|
}
|
||||||
|
Expr2::Blank => mark_node_pool.add(new_blank_mn(ast_node_id, None)),
|
||||||
|
Expr2::LetValue {
|
||||||
|
def_id,
|
||||||
|
body_id: _,
|
||||||
|
body_var: _,
|
||||||
|
} => {
|
||||||
|
let pattern_id = env.pool.get(*def_id).get_pattern_id();
|
||||||
|
|
||||||
|
let pattern2 = env.pool.get(pattern_id);
|
||||||
|
|
||||||
|
let val_name = get_identifier_string(pattern2, interns)?;
|
||||||
|
|
||||||
|
let val_name_mn = MarkupNode::Text {
|
||||||
|
content: val_name,
|
||||||
|
ast_node_id,
|
||||||
|
syn_high_style: HighlightStyle::Variable,
|
||||||
|
attributes: Attributes::new(),
|
||||||
|
parent_id_opt: None,
|
||||||
|
newlines_at_end: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let val_name_mn_id = mark_node_pool.add(val_name_mn);
|
||||||
|
|
||||||
|
let equals_mn_id = mark_node_pool.add(new_equals_mn(ast_node_id, None));
|
||||||
|
|
||||||
|
let value_def = env.pool.get(*def_id);
|
||||||
|
|
||||||
|
match value_def {
|
||||||
|
ValueDef::NoAnnotation {
|
||||||
|
pattern_id: _,
|
||||||
|
expr_id,
|
||||||
|
expr_var: _,
|
||||||
|
} => {
|
||||||
|
let body_mn_id = expr2_to_markup(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
env.pool.get(*expr_id),
|
||||||
|
*expr_id,
|
||||||
|
mark_node_pool,
|
||||||
|
interns,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let body_mn = mark_node_pool.get_mut(body_mn_id);
|
||||||
|
body_mn.add_newline_at_end();
|
||||||
|
|
||||||
|
let full_let_node = MarkupNode::Nested {
|
||||||
|
ast_node_id,
|
||||||
|
children_ids: vec![val_name_mn_id, equals_mn_id, body_mn_id],
|
||||||
|
parent_id_opt: None,
|
||||||
|
newlines_at_end: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
mark_node_pool.add(full_let_node)
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
unimplemented!(
|
||||||
|
"I don't know how to convert {:?} into a MarkupNode yet.",
|
||||||
|
other
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr2::Blank => markup_node_pool.add(MarkupNode::Blank {
|
|
||||||
ast_node_id: expr2_node_id,
|
|
||||||
attributes: Attributes::new(),
|
|
||||||
syn_high_style: HighlightStyle::Blank,
|
|
||||||
parent_id_opt: None,
|
|
||||||
}),
|
|
||||||
Expr2::RuntimeError() => new_markup_node(
|
Expr2::RuntimeError() => new_markup_node(
|
||||||
"RunTimeError".to_string(),
|
"RunTimeError".to_string(),
|
||||||
expr2_node_id,
|
ast_node_id,
|
||||||
HighlightStyle::Blank,
|
HighlightStyle::Blank,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
),
|
),
|
||||||
rest => todo!("implement expr2_to_markup for {:?}", rest),
|
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) {
|
pub fn set_parent_for_all(markup_node_id: MarkNodeId, mark_node_pool: &mut SlowPool) {
|
||||||
let node = markup_node_pool.get(markup_node_id);
|
let node = mark_node_pool.get(markup_node_id);
|
||||||
|
|
||||||
if let MarkupNode::Nested {
|
if let MarkupNode::Nested {
|
||||||
ast_node_id: _,
|
ast_node_id: _,
|
||||||
children_ids,
|
children_ids,
|
||||||
parent_id_opt: _,
|
parent_id_opt: _,
|
||||||
|
newlines_at_end: _,
|
||||||
} = node
|
} = node
|
||||||
{
|
{
|
||||||
// need to clone because of borrowing issues
|
// need to clone because of borrowing issues
|
||||||
let children_ids_clone = children_ids.clone();
|
let children_ids_clone = children_ids.clone();
|
||||||
|
|
||||||
for child_id in 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(
|
pub fn set_parent_for_all_helper(
|
||||||
markup_node_id: MarkNodeId,
|
markup_node_id: MarkNodeId,
|
||||||
parent_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 {
|
match node {
|
||||||
MarkupNode::Nested {
|
MarkupNode::Nested {
|
||||||
|
@ -479,7 +593,7 @@ pub fn set_parent_for_all_helper(
|
||||||
let children_ids_clone = children_ids.clone();
|
let children_ids_clone = children_ids.clone();
|
||||||
|
|
||||||
for child_id in 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),
|
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 {
|
impl fmt::Display for MarkupNode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{} ({})",
|
"{} ({}, {})",
|
||||||
self.node_type_as_string(),
|
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 {
|
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);
|
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);
|
tree_as_string_helper(node, 1, &mut full_string, mark_node_pool);
|
||||||
|
|
||||||
|
@ -524,11 +852,25 @@ fn tree_as_string_helper(
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
let child = mark_node_pool.get(child_id);
|
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_string.push_str(&full_str);
|
||||||
|
|
||||||
tree_as_string_helper(child, level + 1, tree_string, mark_node_pool);
|
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 code_lines;
|
||||||
mod config;
|
mod config;
|
||||||
mod ed_error;
|
pub mod ed_error;
|
||||||
mod grid_node_map;
|
mod grid_node_map;
|
||||||
mod keyboard_input;
|
mod keyboard_input;
|
||||||
pub mod main;
|
pub mod main;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::app_model::AppModel;
|
use super::app_model::AppModel;
|
||||||
use super::ed_update;
|
use super::ed_update;
|
||||||
use crate::editor::ed_error::EdResult;
|
|
||||||
use crate::window::keyboard_input::Modifiers;
|
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<()> {
|
pub fn handle_copy(app_model: &mut AppModel) -> EdResult<()> {
|
||||||
if let Some(ref mut ed_model) = app_model.ed_model_opt {
|
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 {
|
pub enum InputOutcome {
|
||||||
Accepted,
|
Accepted,
|
||||||
Ignored,
|
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 let Some(ref mut ed_model) = app_model.ed_model_opt {
|
||||||
if ed_model.has_focus {
|
if ed_model.has_focus {
|
||||||
return ed_update::handle_new_char(received_char, ed_model);
|
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::code_lines::CodeLines;
|
||||||
use crate::editor::grid_node_map::GridNodeMap;
|
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::slow_pool::{MarkNodeId, SlowPool};
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
|
||||||
use crate::editor::{
|
use crate::editor::{
|
||||||
ed_error::EdError::ParseError,
|
ed_error::SrcParseError,
|
||||||
ed_error::{EdResult, MissingParent, NoNodeAtCaretPosition},
|
ed_error::{EdResult, EmptyCodeString, MissingParent, NoNodeAtCaretPosition},
|
||||||
markup::attribute::Attributes,
|
|
||||||
markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode},
|
|
||||||
};
|
};
|
||||||
use crate::graphics::primitives::rect::Rect;
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use crate::lang::ast::{Expr2, ExprId};
|
use crate::lang::expr::Env;
|
||||||
use crate::lang::expr::{str_to_expr2, Env};
|
use crate::lang::parse::{ASTNodeId, AST};
|
||||||
use crate::lang::pool::PoolStr;
|
use crate::lang::pool::PoolStr;
|
||||||
use crate::lang::scope::Scope;
|
use crate::ui::text::caret_w_select::{CaretPos, CaretWSelect};
|
||||||
use crate::ui::text::caret_w_select::CaretWSelect;
|
|
||||||
use crate::ui::text::lines::SelectableLines;
|
use crate::ui::text::lines::SelectableLines;
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
use bumpalo::collections::String as BumpString;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use roc_module::symbol::Interns;
|
use roc_load::file::LoadedModule;
|
||||||
use roc_region::all::Region;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -31,83 +26,98 @@ pub struct EdModel<'a> {
|
||||||
pub code_lines: CodeLines,
|
pub code_lines: CodeLines,
|
||||||
// allows us to map window coordinates to MarkNodeId's
|
// allows us to map window coordinates to MarkNodeId's
|
||||||
pub grid_node_map: GridNodeMap,
|
pub grid_node_map: GridNodeMap,
|
||||||
pub markup_root_id: MarkNodeId,
|
pub markup_ids: Vec<MarkNodeId>, // one root node for every expression
|
||||||
pub markup_node_pool: SlowPool,
|
pub mark_node_pool: SlowPool,
|
||||||
// contains single char dimensions, used to calculate line height, column width...
|
// contains single char dimensions, used to calculate line height, column width...
|
||||||
pub glyph_dim_rect_opt: Option<Rect>,
|
pub glyph_dim_rect_opt: Option<Rect>,
|
||||||
pub has_focus: bool,
|
pub has_focus: bool,
|
||||||
pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option<MarkNodeId>)>,
|
pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option<MarkNodeId>)>,
|
||||||
pub selected_expr_opt: Option<SelectedExpression>,
|
pub selected_block_opt: Option<SelectedBlock>,
|
||||||
pub interns: &'a Interns, // this should eventually come from LoadedModule, see #1442
|
pub loaded_module: LoadedModule,
|
||||||
pub show_debug_view: bool,
|
pub show_debug_view: bool,
|
||||||
// EdModel is dirty if it has changed since the previous render.
|
// EdModel is dirty if it has changed since the previous render.
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct SelectedExpression {
|
pub struct SelectedBlock {
|
||||||
pub ast_node_id: ExprId,
|
pub ast_node_id: ASTNodeId,
|
||||||
pub mark_node_id: MarkNodeId,
|
pub mark_node_id: MarkNodeId,
|
||||||
pub type_str: PoolStr,
|
pub type_str: PoolStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_model<'a>(
|
pub fn init_model<'a>(
|
||||||
code_str: &'a BumpString,
|
code_str: &'a str,
|
||||||
file_path: &'a Path,
|
file_path: &'a Path,
|
||||||
env: Env<'a>,
|
env: Env<'a>,
|
||||||
interns: &'a Interns,
|
loaded_module: LoadedModule,
|
||||||
code_arena: &'a Bump,
|
code_arena: &'a Bump,
|
||||||
|
caret_pos: CaretPos, // to set caret position
|
||||||
) -> EdResult<EdModel<'a>> {
|
) -> EdResult<EdModel<'a>> {
|
||||||
let mut module = EdModule::new(code_str, env, code_arena)?;
|
let mut module = EdModule::new(code_str, env, code_arena)?;
|
||||||
|
|
||||||
let ast_root_id = module.ast_root_id;
|
let mut mark_node_pool = SlowPool::new();
|
||||||
let mut markup_node_pool = SlowPool::new();
|
|
||||||
|
|
||||||
let markup_root_id = if code_str.is_empty() {
|
let markup_ids = if code_str.is_empty() {
|
||||||
let blank_root = MarkupNode::Blank {
|
EmptyCodeString {}.fail()
|
||||||
ast_node_id: ast_root_id,
|
|
||||||
attributes: Attributes::new(),
|
|
||||||
syn_high_style: HighlightStyle::Blank,
|
|
||||||
parent_id_opt: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
markup_node_pool.add(blank_root)
|
|
||||||
} else {
|
} else {
|
||||||
let ast_root = &module.env.pool.get(ast_root_id);
|
ast_to_mark_nodes(
|
||||||
|
|
||||||
let temp_markup_root_id = expr2_to_markup(
|
|
||||||
code_arena,
|
code_arena,
|
||||||
&mut module.env,
|
&mut module.env,
|
||||||
ast_root,
|
&module.ast,
|
||||||
ast_root_id,
|
&mut mark_node_pool,
|
||||||
&mut markup_node_pool,
|
&loaded_module.interns,
|
||||||
);
|
)
|
||||||
set_parent_for_all(temp_markup_root_id, &mut markup_node_pool);
|
}?;
|
||||||
|
|
||||||
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 {
|
Ok(EdModel {
|
||||||
module,
|
module,
|
||||||
file_path,
|
file_path,
|
||||||
code_lines,
|
code_lines,
|
||||||
grid_node_map,
|
grid_node_map,
|
||||||
markup_root_id,
|
markup_ids,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
glyph_dim_rect_opt: None,
|
glyph_dim_rect_opt: None,
|
||||||
has_focus: true,
|
has_focus: true,
|
||||||
caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)),
|
caret_w_select_vec: NonEmpty::new((caret, None)),
|
||||||
selected_expr_opt: None,
|
selected_block_opt: None,
|
||||||
interns,
|
loaded_module,
|
||||||
show_debug_view: false,
|
show_debug_view: false,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EdModel<'a> {
|
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> {
|
pub fn get_curr_mark_node_id(&self) -> UIResult<MarkNodeId> {
|
||||||
let caret_pos = self.get_caret();
|
let caret_pos = self.get_caret();
|
||||||
self.grid_node_map.get_id_at_row_col(caret_pos)
|
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)> {
|
pub fn get_curr_child_indices(&self) -> EdResult<(usize, usize)> {
|
||||||
if self.node_exists_at_caret() {
|
if self.node_exists_at_caret() {
|
||||||
let curr_mark_node_id = self.get_curr_mark_node_id()?;
|
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() {
|
if let Some(parent_id) = curr_mark_node.get_parent_id_opt() {
|
||||||
let parent = self.markup_node_pool.get(parent_id);
|
let parent = self.mark_node_pool.get(parent_id);
|
||||||
parent.get_child_indices(curr_mark_node_id, &self.markup_node_pool)
|
parent.get_child_indices(curr_mark_node_id, &self.mark_node_pool)
|
||||||
} else {
|
} else {
|
||||||
MissingParent {
|
MissingParent {
|
||||||
node_id: curr_mark_node_id,
|
node_id: curr_mark_node_id,
|
||||||
|
@ -161,38 +171,26 @@ impl<'a> EdModel<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EdModule<'a> {
|
pub struct EdModule<'a> {
|
||||||
pub env: Env<'a>,
|
pub env: Env<'a>,
|
||||||
pub ast_root_id: ExprId,
|
pub ast: AST,
|
||||||
}
|
}
|
||||||
|
|
||||||
// for debugging
|
// for debugging
|
||||||
// use crate::lang::ast::expr2_to_string;
|
//use crate::lang::ast::expr2_to_string;
|
||||||
|
|
||||||
impl<'a> EdModule<'a> {
|
impl<'a> EdModule<'a> {
|
||||||
pub fn new(code_str: &'a str, mut env: Env<'a>, ast_arena: &'a Bump) -> EdResult<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() {
|
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);
|
match parse_res {
|
||||||
|
Ok(ast) => Ok(EdModule { env, ast }),
|
||||||
let expr2_result = str_to_expr2(ast_arena, code_str, &mut env, &mut scope, region);
|
Err(err) => SrcParseError {
|
||||||
|
|
||||||
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 {
|
|
||||||
syntax_err: format!("{:?}", err),
|
syntax_err: format!("{:?}", err),
|
||||||
}),
|
}
|
||||||
|
.fail(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ast_root_id = env.pool.add(Expr2::Blank);
|
EmptyCodeString {}.fail()
|
||||||
|
|
||||||
Ok(EdModule { env, ast_root_id })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,40 +198,49 @@ impl<'a> EdModule<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_ed_model {
|
pub mod test_ed_model {
|
||||||
use crate::editor::ed_error::EdResult;
|
use crate::editor::ed_error::EdResult;
|
||||||
|
use crate::editor::main::load_module;
|
||||||
use crate::editor::mvc::ed_model;
|
use crate::editor::mvc::ed_model;
|
||||||
|
use crate::editor::resources::strings::HELLO_WORLD;
|
||||||
use crate::lang::expr::Env;
|
use crate::lang::expr::Env;
|
||||||
use crate::lang::pool::Pool;
|
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_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::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::lines::SelectableLines;
|
||||||
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
use bumpalo::collections::String as BumpString;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use ed_model::EdModel;
|
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 roc_types::subs::VarStore;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tempfile::tempdir;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn init_dummy_model<'a>(
|
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,
|
ed_model_refs: &'a mut EdModelRefs,
|
||||||
|
code_arena: &'a Bump,
|
||||||
) -> EdResult<EdModel<'a>> {
|
) -> EdResult<EdModel<'a>> {
|
||||||
let file_path = Path::new("");
|
let file_path = Path::new("");
|
||||||
|
|
||||||
let dep_idents = IdentIds::exposed_builtins(8);
|
let dep_idents = IdentIds::exposed_builtins(8);
|
||||||
let exposed_ident_ids = IdentIds::default();
|
let exposed_ident_ids = IdentIds::default();
|
||||||
let mod_id = ed_model_refs
|
|
||||||
.interns
|
|
||||||
.module_ids
|
|
||||||
.get_or_insert(&"ModId123".into());
|
|
||||||
|
|
||||||
let env = Env::new(
|
let env = Env::new(
|
||||||
mod_id,
|
loaded_module.module_id,
|
||||||
&ed_model_refs.env_arena,
|
&ed_model_refs.env_arena,
|
||||||
&mut ed_model_refs.env_pool,
|
&mut ed_model_refs.env_pool,
|
||||||
&mut ed_model_refs.var_store,
|
&mut ed_model_refs.var_store,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
&ed_model_refs.interns.module_ids,
|
module_ids,
|
||||||
exposed_ident_ids,
|
exposed_ident_ids,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -241,43 +248,69 @@ pub mod test_ed_model {
|
||||||
code_str,
|
code_str,
|
||||||
file_path,
|
file_path,
|
||||||
env,
|
env,
|
||||||
&ed_model_refs.interns,
|
loaded_module,
|
||||||
&ed_model_refs.code_arena,
|
code_arena,
|
||||||
|
CaretPos::End,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EdModelRefs {
|
pub struct EdModelRefs {
|
||||||
code_arena: Bump,
|
|
||||||
env_arena: Bump,
|
env_arena: Bump,
|
||||||
env_pool: Pool,
|
env_pool: Pool,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
interns: Interns,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_model_refs() -> EdModelRefs {
|
pub fn init_model_refs() -> EdModelRefs {
|
||||||
EdModelRefs {
|
EdModelRefs {
|
||||||
code_arena: Bump::new(),
|
|
||||||
env_arena: Bump::new(),
|
env_arena: Bump::new(),
|
||||||
env_pool: Pool::with_capacity(1024),
|
env_pool: Pool::with_capacity(1024),
|
||||||
var_store: VarStore::default(),
|
var_store: VarStore::default(),
|
||||||
interns: Interns {
|
|
||||||
module_ids: ModuleIds::default(),
|
|
||||||
all_ident_ids: IdentIds::exposed_builtins(8),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ed_model_from_dsl<'a>(
|
pub fn ed_model_from_dsl<'a>(
|
||||||
clean_code_str: &'a BumpString,
|
clean_code_str: &'a mut String,
|
||||||
code_lines: &[&str],
|
code_lines: Vec<String>,
|
||||||
ed_model_refs: &'a mut EdModelRefs,
|
ed_model_refs: &'a mut EdModelRefs,
|
||||||
|
module_ids: &'a ModuleIds,
|
||||||
|
code_arena: &'a Bump,
|
||||||
) -> Result<EdModel<'a>, String> {
|
) -> Result<EdModel<'a>, String> {
|
||||||
let code_lines_vec: Vec<String> = (*code_lines).iter().map(|s| s.to_string()).collect();
|
let full_code = vec![HELLO_WORLD, clean_code_str.as_str()];
|
||||||
let caret_w_select = convert_dsl_to_selection(&code_lines_vec)?;
|
*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)
|
Ok(ed_model)
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
use super::ed_model::EdModel;
|
use super::ed_model::EdModel;
|
||||||
use crate::editor::config::Config;
|
use crate::editor::config::Config;
|
||||||
use crate::editor::ed_error::EdResult;
|
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_ast::build_code_graphics;
|
||||||
use crate::editor::render_debug::build_debug_graphics;
|
use crate::editor::render_debug::build_debug_graphics;
|
||||||
use crate::editor::resources::strings::START_TIP;
|
use crate::editor::resources::strings::START_TIP;
|
||||||
|
@ -20,33 +20,49 @@ use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RenderedWgpu {
|
pub struct RenderedWgpu {
|
||||||
pub text_sections: Vec<glyph_brush::OwnedSection>,
|
pub text_sections_behind: Vec<glyph_brush::OwnedSection>, // displayed in front of rect_behind, behind everything else
|
||||||
pub rects: Vec<Rect>,
|
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 {
|
impl RenderedWgpu {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
text_sections: Vec::new(),
|
text_sections_behind: Vec::new(),
|
||||||
rects: 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) {
|
pub fn add_text_behind(&mut self, new_text_section: glyph_brush::OwnedSection) {
|
||||||
self.text_sections.push(new_text_section);
|
self.text_sections_behind.push(new_text_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rect(&mut self, new_rect: Rect) {
|
pub fn add_text_front(&mut self, new_text_section: glyph_brush::OwnedSection) {
|
||||||
self.rects.push(new_rect);
|
self.text_sections_front.push(new_text_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_rects(&mut self, new_rects: Vec<Rect>) {
|
pub fn add_rect_behind(&mut self, new_rect: Rect) {
|
||||||
self.rects.extend(new_rects);
|
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) {
|
pub fn extend(&mut self, rendered_wgpu: RenderedWgpu) {
|
||||||
self.text_sections.extend(rendered_wgpu.text_sections);
|
self.text_sections_behind
|
||||||
self.rects.extend(rendered_wgpu.rects);
|
.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 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 {
|
let start_tip_text = owned_section_from_text(&Text {
|
||||||
position: tip_txt_coords.into(),
|
position: tip_txt_coords.into(),
|
||||||
|
@ -72,15 +91,15 @@ pub fn model_to_wgpu<'a>(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
all_rendered.add_text(start_tip_text);
|
all_rendered.add_text_behind(start_tip_text);
|
||||||
|
|
||||||
let rendered_code_graphics = build_code_graphics(
|
let rendered_code_graphics = build_code_graphics(
|
||||||
ed_model.markup_node_pool.get(ed_model.markup_root_id),
|
&ed_model.markup_ids,
|
||||||
size,
|
size,
|
||||||
txt_coords,
|
txt_coords,
|
||||||
config,
|
config,
|
||||||
glyph_dim_rect,
|
glyph_dim_rect,
|
||||||
&ed_model.markup_node_pool,
|
&ed_model.mark_node_pool,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
all_rendered.extend(rendered_code_graphics);
|
all_rendered.extend(rendered_code_graphics);
|
||||||
|
@ -93,7 +112,7 @@ pub fn model_to_wgpu<'a>(
|
||||||
|
|
||||||
let rendered_selection = build_selection_graphics(
|
let rendered_selection = build_selection_graphics(
|
||||||
caret_w_sel_vec,
|
caret_w_sel_vec,
|
||||||
&ed_model.selected_expr_opt,
|
&ed_model.selected_block_opt,
|
||||||
txt_coords,
|
txt_coords,
|
||||||
config,
|
config,
|
||||||
glyph_dim_rect,
|
glyph_dim_rect,
|
||||||
|
@ -103,7 +122,7 @@ pub fn model_to_wgpu<'a>(
|
||||||
all_rendered.extend(rendered_selection);
|
all_rendered.extend(rendered_selection);
|
||||||
|
|
||||||
if ed_model.show_debug_view {
|
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)
|
Ok(all_rendered)
|
||||||
|
@ -111,7 +130,7 @@ pub fn model_to_wgpu<'a>(
|
||||||
|
|
||||||
pub fn build_selection_graphics(
|
pub fn build_selection_graphics(
|
||||||
caret_w_select_vec: Vec<CaretWSelect>,
|
caret_w_select_vec: Vec<CaretWSelect>,
|
||||||
selected_expr_opt: &Option<SelectedExpression>,
|
selected_expr_opt: &Option<SelectedBlock>,
|
||||||
txt_coords: Vector2<f32>,
|
txt_coords: Vector2<f32>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
glyph_dim_rect: Rect,
|
glyph_dim_rect: Rect,
|
||||||
|
@ -139,7 +158,7 @@ pub fn build_selection_graphics(
|
||||||
let width =
|
let width =
|
||||||
((end_pos.column as f32) * char_width) - ((start_pos.column as f32) * char_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_x,
|
||||||
sel_rect_y,
|
sel_rect_y,
|
||||||
width,
|
width,
|
||||||
|
@ -158,12 +177,12 @@ pub fn build_selection_graphics(
|
||||||
let (tip_rect, tip_text) =
|
let (tip_rect, tip_text) =
|
||||||
tooltip.render_tooltip(&glyph_dim_rect, &config.ed_theme.ui_theme);
|
tooltip.render_tooltip(&glyph_dim_rect, &config.ed_theme.ui_theme);
|
||||||
|
|
||||||
all_rendered.add_rect(tip_rect);
|
all_rendered.add_rect_front(tip_rect);
|
||||||
all_rendered.add_text(tip_text);
|
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_x,
|
||||||
top_left_y,
|
top_left_y,
|
||||||
&glyph_dim_rect,
|
&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)?;
|
} = get_node_context(ed_model)?;
|
||||||
|
|
||||||
let is_blank_node = curr_mark_node.is_blank();
|
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();
|
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),
|
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 {
|
let int_node = MarkupNode::Text {
|
||||||
content: digit_string,
|
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,
|
syn_high_style: HighlightStyle::Number,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt,
|
parent_id_opt,
|
||||||
|
newlines_at_end: curr_mark_node_nls,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_blank_node {
|
if is_blank_node {
|
||||||
ed_model
|
ed_model
|
||||||
.markup_node_pool
|
.mark_node_pool
|
||||||
.replace_node(curr_mark_node_id, int_node);
|
.replace_node(curr_mark_node_id, int_node);
|
||||||
|
|
||||||
// remove data corresponding to Blank 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);
|
ed_model.simple_move_carets_right(char_len);
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
&digit_char.to_string(),
|
&digit_char.to_string(),
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(InputOutcome::Accepted)
|
Ok(InputOutcome::Accepted)
|
||||||
|
@ -85,7 +93,7 @@ pub fn update_int(
|
||||||
.grid_node_map
|
.grid_node_map
|
||||||
.get_offset_to_node_id(old_caret_pos, int_mark_node_id)?;
|
.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 int_ast_node_id = int_mark_node.get_ast_node_id();
|
||||||
|
|
||||||
let content_str_mut = int_mark_node.get_content_mut()?;
|
let content_str_mut = int_mark_node.get_content_mut()?;
|
||||||
|
@ -98,19 +106,25 @@ pub fn update_int(
|
||||||
} else {
|
} else {
|
||||||
content_str_mut.insert(node_caret_offset, *ch);
|
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
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
&ch.to_string(),
|
&ch.to_string(),
|
||||||
int_mark_node_id,
|
int_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// update ast
|
// update ast
|
||||||
let new_pool_str = PoolStr::new(&content_str, ed_model.module.env.pool);
|
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 {
|
match int_ast_node {
|
||||||
SmallInt { number, text, .. } => {
|
SmallInt { number, text, .. } => {
|
||||||
update_small_int_num(number, &content_str)?;
|
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::EdResult;
|
||||||
use crate::editor::ed_error::{MissingParent, UnexpectedASTNode};
|
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;
|
||||||
use crate::editor::markup::nodes::MarkupNode;
|
use crate::editor::markup::nodes::MarkupNode;
|
||||||
use crate::editor::mvc::app_update::InputOutcome;
|
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::get_node_context;
|
||||||
use crate::editor::mvc::ed_update::NodeContext;
|
use crate::editor::mvc::ed_update::NodeContext;
|
||||||
use crate::editor::slow_pool::MarkNodeId;
|
use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::lang::ast::ExprId;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::{ast_node_to_string, Expr2};
|
||||||
use crate::lang::ast::{expr2_to_string, ExprId};
|
use crate::lang::parse::ASTNodeId;
|
||||||
use crate::lang::pool::PoolVec;
|
use crate::lang::pool::PoolVec;
|
||||||
use crate::ui::text::text_pos::TextPos;
|
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)?;
|
} = get_node_context(ed_model)?;
|
||||||
|
|
||||||
let is_blank_node = curr_mark_node.is_blank();
|
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 {
|
let expr2_node = Expr2::List {
|
||||||
elem_var: ed_model.module.env.var_store.fresh(),
|
elem_var: ed_model.module.env.var_store.fresh(),
|
||||||
elems: PoolVec::empty(ed_model.module.env.pool),
|
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 {
|
let right_bracket_node_id = ed_model.add_mark_node(new_right_square_mn(
|
||||||
content: nodes::LEFT_SQUARE_BR.to_owned(),
|
ast_node_id.to_expr_id()?,
|
||||||
ast_node_id,
|
Some(curr_mark_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 nested_node = MarkupNode::Nested {
|
let nested_node = MarkupNode::Nested {
|
||||||
ast_node_id,
|
ast_node_id,
|
||||||
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
||||||
parent_id_opt,
|
parent_id_opt,
|
||||||
|
newlines_at_end: curr_mark_node_nls,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_blank_node {
|
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_blank_expr_node(old_caret_pos)?;
|
||||||
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
|
|
||||||
|
|
||||||
ed_model.simple_move_carets_right(nodes::LEFT_SQUARE_BR.len());
|
ed_model.simple_move_carets_right(nodes::LEFT_SQUARE_BR.len());
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
nodes::LEFT_SQUARE_BR,
|
nodes::LEFT_SQUARE_BR,
|
||||||
left_bracket_node_id,
|
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.line,
|
||||||
old_caret_pos.column + nodes::LEFT_SQUARE_BR.len(),
|
old_caret_pos.column + nodes::LEFT_SQUARE_BR.len(),
|
||||||
nodes::RIGHT_SQUARE_BR,
|
nodes::RIGHT_SQUARE_BR,
|
||||||
right_bracket_node_id,
|
right_bracket_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(InputOutcome::Accepted)
|
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 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_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 {
|
match list_ast_node {
|
||||||
Expr2::List {
|
Expr2::List {
|
||||||
|
@ -118,11 +119,11 @@ pub fn add_blank_child(
|
||||||
let blank_elt = Expr2::Blank;
|
let blank_elt = Expr2::Blank;
|
||||||
let blank_elt_id = ed_model.module.env.pool.add(blank_elt);
|
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 {
|
_ => UnexpectedASTNode {
|
||||||
required_node_type: "List".to_string(),
|
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(),
|
.fail(),
|
||||||
}
|
}
|
||||||
|
@ -159,7 +160,7 @@ pub fn add_blank_child(
|
||||||
}
|
}
|
||||||
_ => UnexpectedASTNode {
|
_ => UnexpectedASTNode {
|
||||||
required_node_type: "List".to_string(),
|
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(),
|
.fail(),
|
||||||
}?;
|
}?;
|
||||||
|
@ -173,7 +174,7 @@ pub fn add_blank_child(
|
||||||
ed_model,
|
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() {
|
for (indx, child) in new_mark_children.iter().enumerate() {
|
||||||
parent.add_child_at_index(new_child_index + indx, *child)?;
|
parent.add_child_at_index(new_child_index + indx, *child)?;
|
||||||
|
@ -191,35 +192,26 @@ pub fn update_mark_children(
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
ed_model: &mut EdModel,
|
ed_model: &mut EdModel,
|
||||||
) -> EdResult<Vec<MarkNodeId>> {
|
) -> EdResult<Vec<MarkNodeId>> {
|
||||||
let blank_mark_node = MarkupNode::Blank {
|
let blank_mark_node_id = ed_model.add_mark_node(new_blank_mn(
|
||||||
ast_node_id: blank_elt_id,
|
ASTNodeId::AExprId(blank_elt_id),
|
||||||
syn_high_style: HighlightStyle::Blank,
|
|
||||||
attributes: Attributes::new(),
|
|
||||||
parent_id_opt,
|
parent_id_opt,
|
||||||
};
|
));
|
||||||
|
|
||||||
let blank_mark_node_id = ed_model.markup_node_pool.add(blank_mark_node);
|
|
||||||
|
|
||||||
let mut children: Vec<MarkNodeId> = vec![];
|
let mut children: Vec<MarkNodeId> = vec![];
|
||||||
|
|
||||||
if new_child_index > 1 {
|
if new_child_index > 1 {
|
||||||
let comma_mark_node = MarkupNode::Text {
|
let comma_mark_node_id =
|
||||||
content: nodes::COMMA.to_owned(),
|
ed_model.add_mark_node(new_comma_mn(list_ast_node_id, parent_id_opt));
|
||||||
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);
|
|
||||||
|
|
||||||
ed_model.simple_move_carets_right(nodes::COMMA.len());
|
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.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
nodes::COMMA,
|
nodes::COMMA,
|
||||||
comma_mark_node_id,
|
comma_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
children.push(comma_mark_node_id);
|
children.push(comma_mark_node_id);
|
||||||
|
@ -234,11 +226,13 @@ pub fn update_mark_children(
|
||||||
};
|
};
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column + comma_shift,
|
old_caret_pos.column + comma_shift,
|
||||||
nodes::BLANK_PLACEHOLDER,
|
nodes::BLANK_PLACEHOLDER,
|
||||||
blank_mark_node_id,
|
blank_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(children)
|
Ok(children)
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub fn update_invalid_lookup(
|
||||||
input_str: &str,
|
input_str: &str,
|
||||||
old_pool_str: &PoolStr,
|
old_pool_str: &PoolStr,
|
||||||
curr_mark_node_id: MarkNodeId,
|
curr_mark_node_id: MarkNodeId,
|
||||||
ast_node_id: ExprId,
|
expr_id: ExprId,
|
||||||
ed_model: &mut EdModel,
|
ed_model: &mut EdModel,
|
||||||
) -> EdResult<InputOutcome> {
|
) -> EdResult<InputOutcome> {
|
||||||
if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
||||||
|
@ -32,10 +32,10 @@ pub fn update_invalid_lookup(
|
||||||
.module
|
.module
|
||||||
.env
|
.env
|
||||||
.pool
|
.pool
|
||||||
.set(ast_node_id, Expr2::InvalidLookup(new_pool_str));
|
.set(expr_id, Expr2::InvalidLookup(new_pool_str));
|
||||||
|
|
||||||
// update MarkupNode
|
// 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 content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||||
content_str_mut.insert_str(caret_offset, input_str);
|
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());
|
ed_model.simple_move_carets_right(input_str.len());
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
input_str,
|
input_str,
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(InputOutcome::Accepted)
|
Ok(InputOutcome::Accepted)
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
pub mod app_model;
|
pub mod app_model;
|
||||||
pub mod app_update;
|
pub mod app_update;
|
||||||
|
mod break_line;
|
||||||
pub mod ed_model;
|
pub mod ed_model;
|
||||||
pub mod ed_update;
|
pub mod ed_update;
|
||||||
pub mod ed_view;
|
pub mod ed_view;
|
||||||
mod int_update;
|
mod int_update;
|
||||||
|
mod let_update;
|
||||||
mod list_update;
|
mod list_update;
|
||||||
mod lookup_update;
|
mod lookup_update;
|
||||||
mod record_update;
|
mod record_update;
|
||||||
mod string_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::MissingParent;
|
||||||
use crate::editor::ed_error::RecordWithoutFields;
|
use crate::editor::ed_error::RecordWithoutFields;
|
||||||
use crate::editor::markup::attribute::Attributes;
|
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;
|
||||||
use crate::editor::markup::nodes::MarkupNode;
|
use crate::editor::markup::nodes::MarkupNode;
|
||||||
use crate::editor::mvc::app_update::InputOutcome;
|
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::syntax_highlight::HighlightStyle;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
||||||
|
use crate::lang::parse::ASTNodeId;
|
||||||
use crate::lang::pool::{PoolStr, PoolVec};
|
use crate::lang::pool::{PoolStr, PoolVec};
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use snafu::OptionExt;
|
use snafu::OptionExt;
|
||||||
|
@ -26,61 +30,44 @@ pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
||||||
} = get_node_context(ed_model)?;
|
} = get_node_context(ed_model)?;
|
||||||
|
|
||||||
let is_blank_node = curr_mark_node.is_blank();
|
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 ast_pool = &mut ed_model.module.env.pool;
|
||||||
let expr2_node = Expr2::EmptyRecord;
|
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 {
|
let right_bracket_node_id = ed_model.add_mark_node(new_right_accolade_mn(
|
||||||
content: nodes::LEFT_ACCOLADE.to_owned(),
|
ast_node_id.to_expr_id()?,
|
||||||
ast_node_id,
|
Some(curr_mark_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 nested_node = MarkupNode::Nested {
|
let nested_node = MarkupNode::Nested {
|
||||||
ast_node_id,
|
ast_node_id,
|
||||||
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
children_ids: vec![left_bracket_node_id, right_bracket_node_id],
|
||||||
parent_id_opt,
|
parent_id_opt,
|
||||||
|
newlines_at_end: curr_mark_node_nls,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_blank_node {
|
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_blank_expr_node(old_caret_pos)?;
|
||||||
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
|
|
||||||
|
|
||||||
ed_model.simple_move_carets_right(nodes::LEFT_ACCOLADE.len());
|
ed_model.simple_move_carets_right(nodes::LEFT_ACCOLADE.len());
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
ed_model.insert_all_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
nodes::LEFT_ACCOLADE,
|
&[left_bracket_node_id, right_bracket_node_id],
|
||||||
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,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(InputOutcome::Accepted)
|
Ok(InputOutcome::Accepted)
|
||||||
|
@ -100,7 +87,7 @@ pub fn update_empty_record(
|
||||||
if input_chars.all(|ch| ch.is_ascii_alphabetic())
|
if input_chars.all(|ch| ch.is_ascii_alphabetic())
|
||||||
&& input_chars.all(|ch| ch.is_ascii_lowercase())
|
&& 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 {
|
let NodeContext {
|
||||||
old_caret_pos,
|
old_caret_pos,
|
||||||
|
@ -110,8 +97,8 @@ pub fn update_empty_record(
|
||||||
ast_node_id,
|
ast_node_id,
|
||||||
} = get_node_context(ed_model)?;
|
} = get_node_context(ed_model)?;
|
||||||
|
|
||||||
if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE
|
if prev_mark_node.get_content() == nodes::LEFT_ACCOLADE
|
||||||
&& curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE
|
&& curr_mark_node.get_content() == nodes::RIGHT_ACCOLADE
|
||||||
{
|
{
|
||||||
// update AST
|
// update AST
|
||||||
let record_var = ed_model.module.env.var_store.fresh();
|
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 };
|
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
|
// update Markup
|
||||||
|
|
||||||
|
@ -134,12 +125,13 @@ pub fn update_empty_record(
|
||||||
syn_high_style: HighlightStyle::RecordField,
|
syn_high_style: HighlightStyle::RecordField,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt,
|
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 {
|
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)?;
|
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);
|
ed_model.simple_move_carets_right(1);
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
new_input,
|
new_input,
|
||||||
record_field_node_id,
|
record_field_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(InputOutcome::Accepted)
|
Ok(InputOutcome::Accepted)
|
||||||
|
@ -183,118 +177,114 @@ pub fn update_record_colon(
|
||||||
ast_node_id,
|
ast_node_id,
|
||||||
} = get_node_context(ed_model)?;
|
} = get_node_context(ed_model)?;
|
||||||
if let Some(parent_id) = parent_id_opt {
|
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()?;
|
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 {
|
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
|
match prev_mark_node.get_ast_node_id() {
|
||||||
.module
|
ASTNodeId::ADefId(_) => Ok(InputOutcome::Ignored),
|
||||||
.env
|
ASTNodeId::AExprId(prev_expr_id) => {
|
||||||
.pool
|
let prev_expr = ed_model.module.env.pool.get(prev_expr_id);
|
||||||
.get(prev_mark_node.get_ast_node_id());
|
|
||||||
|
|
||||||
// current and prev node should always point to record when in valid position to add ':'
|
// 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 { .. })
|
&& 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)?;
|
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
|
||||||
|
|
||||||
let ast_node_ref = ed_model.module.env.pool.get(record_ast_node_id);
|
let ast_node_ref = ed_model.module.env.pool.get(record_ast_node_id);
|
||||||
|
|
||||||
match ast_node_ref {
|
match ast_node_ref {
|
||||||
Expr2::Record {
|
Expr2::Record {
|
||||||
record_var: _,
|
record_var: _,
|
||||||
fields,
|
fields,
|
||||||
} => {
|
} => {
|
||||||
if ed_model.node_exists_at_caret() {
|
if ed_model.node_exists_at_caret() {
|
||||||
let next_mark_node_id =
|
let next_mark_node_id =
|
||||||
ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?;
|
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);
|
let next_mark_node =
|
||||||
if next_mark_node.get_content()? == nodes::RIGHT_ACCOLADE {
|
ed_model.mark_node_pool.get(next_mark_node_id);
|
||||||
// update AST node
|
if next_mark_node.get_content() == nodes::RIGHT_ACCOLADE {
|
||||||
let new_field_val = Expr2::Blank;
|
// update AST node
|
||||||
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
|
let new_field_val = Expr2::Blank;
|
||||||
|
let new_field_val_id =
|
||||||
|
ed_model.module.env.pool.add(new_field_val);
|
||||||
|
|
||||||
let first_field_mut = fields
|
let first_field_mut = fields
|
||||||
.iter_mut(ed_model.module.env.pool)
|
.iter_mut(ed_model.module.env.pool)
|
||||||
.next()
|
.next()
|
||||||
.with_context(|| RecordWithoutFields {})?;
|
.with_context(|| RecordWithoutFields {})?;
|
||||||
|
|
||||||
*first_field_mut = RecordField::LabeledValue(
|
*first_field_mut = RecordField::LabeledValue(
|
||||||
*first_field_mut.get_record_field_pool_str(),
|
*first_field_mut.get_record_field_pool_str(),
|
||||||
*first_field_mut.get_record_field_var(),
|
*first_field_mut.get_record_field_var(),
|
||||||
new_field_val_id,
|
new_field_val_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// update Markup
|
// update Markup
|
||||||
let record_colon = nodes::COLON;
|
let record_colon = nodes::COLON;
|
||||||
|
|
||||||
let record_colon_node = MarkupNode::Text {
|
let record_colon_node = MarkupNode::Text {
|
||||||
content: record_colon.to_owned(),
|
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,
|
syn_high_style: HighlightStyle::Operator,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt: Some(parent_id),
|
parent_id_opt: Some(parent_id),
|
||||||
};
|
newlines_at_end: 0,
|
||||||
|
};
|
||||||
|
|
||||||
let record_colon_node_id =
|
let record_colon_node_id =
|
||||||
ed_model.markup_node_pool.add(record_colon_node);
|
ed_model.add_mark_node(record_colon_node);
|
||||||
ed_model
|
ed_model
|
||||||
.markup_node_pool
|
.mark_node_pool
|
||||||
.get_mut(parent_id)
|
.get_mut(parent_id)
|
||||||
.add_child_at_index(new_child_index, record_colon_node_id)?;
|
.add_child_at_index(
|
||||||
|
new_child_index,
|
||||||
|
record_colon_node_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
let record_blank_node = MarkupNode::Blank {
|
let record_blank_node_id =
|
||||||
ast_node_id: new_field_val_id,
|
ed_model.add_mark_node(new_blank_mn(
|
||||||
syn_high_style: HighlightStyle::Blank,
|
ASTNodeId::AExprId(new_field_val_id),
|
||||||
attributes: Attributes::new(),
|
Some(parent_id),
|
||||||
parent_id_opt: Some(parent_id),
|
));
|
||||||
};
|
|
||||||
|
|
||||||
let record_blank_node_id =
|
ed_model
|
||||||
ed_model.markup_node_pool.add(record_blank_node);
|
.mark_node_pool
|
||||||
ed_model
|
.get_mut(parent_id)
|
||||||
.markup_node_pool
|
.add_child_at_index(
|
||||||
.get_mut(parent_id)
|
new_child_index + 1,
|
||||||
.add_child_at_index(
|
record_blank_node_id,
|
||||||
new_child_index + 1,
|
)?;
|
||||||
record_blank_node_id,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// update caret
|
// update caret
|
||||||
ed_model.simple_move_carets_right(record_colon.len());
|
ed_model.simple_move_carets_right(record_colon.len());
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
ed_model.insert_all_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
nodes::COLON,
|
&[record_colon_node_id, record_blank_node_id],
|
||||||
record_colon_node_id,
|
)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
ed_model.insert_between_line(
|
Ok(InputOutcome::Accepted)
|
||||||
old_caret_pos.line,
|
} else {
|
||||||
old_caret_pos.column + nodes::COLON.len(),
|
Ok(InputOutcome::Ignored)
|
||||||
nodes::BLANK_PLACEHOLDER,
|
}
|
||||||
record_blank_node_id,
|
} else {
|
||||||
)?;
|
Ok(InputOutcome::Ignored)
|
||||||
|
}
|
||||||
Ok(InputOutcome::Accepted)
|
|
||||||
} else {
|
|
||||||
Ok(InputOutcome::Ignored)
|
|
||||||
}
|
}
|
||||||
} else {
|
_ => Ok(InputOutcome::Ignored),
|
||||||
Ok(InputOutcome::Ignored)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Ok(InputOutcome::Ignored)
|
||||||
}
|
}
|
||||||
_ => Ok(InputOutcome::Ignored),
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Ok(InputOutcome::Ignored)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(InputOutcome::Ignored)
|
Ok(InputOutcome::Ignored)
|
||||||
|
@ -315,7 +305,7 @@ pub fn update_record_field(
|
||||||
ed_model: &mut EdModel,
|
ed_model: &mut EdModel,
|
||||||
) -> EdResult<InputOutcome> {
|
) -> EdResult<InputOutcome> {
|
||||||
// update MarkupNode
|
// 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 content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||||
let node_caret_offset = ed_model
|
let node_caret_offset = ed_model
|
||||||
.grid_node_map
|
.grid_node_map
|
||||||
|
@ -337,11 +327,13 @@ pub fn update_record_field(
|
||||||
ed_model.simple_move_carets_right(new_input.len());
|
ed_model.simple_move_carets_right(new_input.len());
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
new_input,
|
new_input,
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// update AST Node
|
// 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::get_node_context;
|
||||||
use crate::editor::mvc::ed_update::NodeContext;
|
use crate::editor::mvc::ed_update::NodeContext;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
|
use crate::lang::ast::update_str_expr;
|
||||||
use crate::lang::ast::ArrString;
|
use crate::lang::ast::ArrString;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::Expr2;
|
||||||
use crate::lang::pool::PoolStr;
|
use crate::lang::pool::PoolStr;
|
||||||
|
@ -27,7 +28,7 @@ pub fn update_small_string(
|
||||||
let new_input = &new_char.to_string();
|
let new_input = &new_char.to_string();
|
||||||
|
|
||||||
// update markup
|
// 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 content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||||
let node_caret_offset = ed_model
|
let node_caret_offset = ed_model
|
||||||
.grid_node_map
|
.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 node_caret_offset != 0 && node_caret_offset < content_str_mut.len() {
|
||||||
if old_array_str.len() < ArrString::capacity() {
|
if old_array_str.len() < ArrString::capacity() {
|
||||||
if let Expr2::SmallStr(ref mut mut_array_str) =
|
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
|
// safe because we checked the length
|
||||||
unsafe {
|
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));
|
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);
|
content_str_mut.insert_str(node_caret_offset, new_input);
|
||||||
|
|
||||||
// update GridNodeMap and CodeLines
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
new_input,
|
new_input,
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// update caret
|
// update caret
|
||||||
|
@ -73,11 +80,7 @@ pub fn update_small_string(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_string(
|
pub fn update_string(new_char: char, ed_model: &mut EdModel) -> EdResult<InputOutcome> {
|
||||||
new_input: &str,
|
|
||||||
old_pool_str: &PoolStr,
|
|
||||||
ed_model: &mut EdModel,
|
|
||||||
) -> EdResult<InputOutcome> {
|
|
||||||
let NodeContext {
|
let NodeContext {
|
||||||
old_caret_pos,
|
old_caret_pos,
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
@ -87,31 +90,32 @@ pub fn update_string(
|
||||||
} = get_node_context(ed_model)?;
|
} = get_node_context(ed_model)?;
|
||||||
|
|
||||||
// update markup
|
// 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 content_str_mut = curr_mark_node_mut.get_content_mut()?;
|
||||||
let node_caret_offset = ed_model
|
let node_caret_offset = ed_model
|
||||||
.grid_node_map
|
.grid_node_map
|
||||||
.get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?;
|
.get_offset_to_node_id(old_caret_pos, curr_mark_node_id)?;
|
||||||
|
|
||||||
if node_caret_offset != 0 && node_caret_offset < content_str_mut.len() {
|
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
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
new_input,
|
&new_char.to_string(),
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// update ast
|
// update ast
|
||||||
let mut new_string = old_pool_str.as_str(ed_model.module.env.pool).to_owned();
|
update_str_expr(
|
||||||
new_string.push_str(new_input);
|
ast_node_id.to_expr_id()?,
|
||||||
|
new_char,
|
||||||
let new_pool_str = PoolStr::new(&new_string, &mut ed_model.module.env.pool);
|
node_caret_offset - 1, // -1 because offset was calculated with quotes
|
||||||
let new_ast_node = Expr2::Str(new_pool_str);
|
&mut ed_model.module.env.pool,
|
||||||
|
)?;
|
||||||
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
|
|
||||||
|
|
||||||
// update caret
|
// update caret
|
||||||
ed_model.simple_move_carets_right(1);
|
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() {
|
if curr_mark_node.is_blank() {
|
||||||
let new_expr2_node = Expr2::SmallStr(arraystring::ArrayString::new());
|
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 {
|
let new_string_node = MarkupNode::Text {
|
||||||
content: nodes::STRING_QUOTES.to_owned(),
|
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,
|
syn_high_style: HighlightStyle::String,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt,
|
parent_id_opt,
|
||||||
|
newlines_at_end: curr_mark_node_nls,
|
||||||
};
|
};
|
||||||
|
|
||||||
ed_model
|
ed_model
|
||||||
.markup_node_pool
|
.mark_node_pool
|
||||||
.replace_node(curr_mark_node_id, new_string_node);
|
.replace_node(curr_mark_node_id, new_string_node);
|
||||||
|
|
||||||
// remove data corresponding to Blank 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
|
// update GridNodeMap and CodeLines
|
||||||
ed_model.insert_between_line(
|
EdModel::insert_between_line(
|
||||||
old_caret_pos.line,
|
old_caret_pos.line,
|
||||||
old_caret_pos.column,
|
old_caret_pos.column,
|
||||||
nodes::STRING_QUOTES,
|
nodes::STRING_QUOTES,
|
||||||
curr_mark_node_id,
|
curr_mark_node_id,
|
||||||
|
&mut ed_model.grid_node_map,
|
||||||
|
&mut ed_model.code_lines,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
ed_model.simple_move_carets_right(1);
|
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::markup::nodes::{MarkupNode, BLANK_PLACEHOLDER};
|
||||||
|
use super::slow_pool::MarkNodeId;
|
||||||
use crate::editor::mvc::ed_view::RenderedWgpu;
|
use crate::editor::mvc::ed_view::RenderedWgpu;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::{ed_error::EdResult, theme::EdTheme, util::map_get};
|
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};
|
use crate::{editor::config::Config, graphics::colors};
|
||||||
|
|
||||||
pub fn build_code_graphics<'a>(
|
pub fn build_code_graphics<'a>(
|
||||||
markup_node: &'a MarkupNode,
|
markup_ids: &[MarkNodeId],
|
||||||
size: &PhysicalSize<u32>,
|
size: &PhysicalSize<u32>,
|
||||||
txt_coords: Vector2<f32>,
|
txt_coords: Vector2<f32>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
glyph_dim_rect: Rect,
|
glyph_dim_rect: Rect,
|
||||||
markup_node_pool: &'a SlowPool,
|
mark_node_pool: &'a SlowPool,
|
||||||
) -> EdResult<RenderedWgpu> {
|
) -> EdResult<RenderedWgpu> {
|
||||||
let area_bounds = (size.width as f32, size.height as f32);
|
let area_bounds = (size.width as f32, size.height as f32);
|
||||||
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
||||||
let mut rendered_wgpu = RenderedWgpu::new();
|
let mut rendered_wgpu = RenderedWgpu::new();
|
||||||
|
|
||||||
let (glyph_text_vec, rects) = markup_to_wgpu(
|
let mut all_glyph_text_vec = vec![];
|
||||||
markup_node,
|
let mut all_rects = vec![];
|
||||||
&CodeStyle {
|
let mut txt_row_col = (0, 0);
|
||||||
ed_theme: &config.ed_theme,
|
|
||||||
font_size: config.code_font_size,
|
for markup_id in markup_ids.iter() {
|
||||||
txt_coords,
|
let mark_node = mark_node_pool.get(*markup_id);
|
||||||
glyph_dim_rect,
|
|
||||||
},
|
let (mut glyph_text_vec, mut rects) = markup_to_wgpu(
|
||||||
markup_node_pool,
|
mark_node,
|
||||||
)?;
|
&CodeStyle {
|
||||||
|
ed_theme: &config.ed_theme,
|
||||||
|
font_size: config.code_font_size,
|
||||||
|
txt_coords,
|
||||||
|
glyph_dim_rect,
|
||||||
|
},
|
||||||
|
&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(
|
let section = gr_text::owned_section_from_glyph_texts(
|
||||||
glyph_text_vec,
|
all_glyph_text_vec,
|
||||||
txt_coords.into(),
|
txt_coords.into(),
|
||||||
area_bounds,
|
area_bounds,
|
||||||
layout,
|
layout,
|
||||||
);
|
);
|
||||||
|
|
||||||
rendered_wgpu.add_rects(rects);
|
rendered_wgpu.add_rects_behind(all_rects); // currently only rects for Blank
|
||||||
rendered_wgpu.add_text(section);
|
rendered_wgpu.add_text_behind(section);
|
||||||
|
|
||||||
Ok(rendered_wgpu)
|
Ok(rendered_wgpu)
|
||||||
}
|
}
|
||||||
|
@ -55,51 +68,57 @@ struct CodeStyle<'a> {
|
||||||
fn markup_to_wgpu<'a>(
|
fn markup_to_wgpu<'a>(
|
||||||
markup_node: &'a MarkupNode,
|
markup_node: &'a MarkupNode,
|
||||||
code_style: &CodeStyle,
|
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>)> {
|
) -> EdResult<(Vec<glyph_brush::OwnedText>, Vec<Rect>)> {
|
||||||
let mut wgpu_texts: Vec<glyph_brush::OwnedText> = Vec::new();
|
let mut wgpu_texts: Vec<glyph_brush::OwnedText> = Vec::new();
|
||||||
let mut rects: Vec<Rect> = Vec::new();
|
let mut rects: Vec<Rect> = Vec::new();
|
||||||
|
|
||||||
let mut txt_row_col = (0, 0);
|
|
||||||
|
|
||||||
markup_to_wgpu_helper(
|
markup_to_wgpu_helper(
|
||||||
markup_node,
|
markup_node,
|
||||||
&mut wgpu_texts,
|
&mut wgpu_texts,
|
||||||
&mut rects,
|
&mut rects,
|
||||||
code_style,
|
code_style,
|
||||||
&mut txt_row_col,
|
txt_row_col,
|
||||||
markup_node_pool,
|
mark_node_pool,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok((wgpu_texts, rects))
|
Ok((wgpu_texts, rects))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use text_row
|
|
||||||
fn markup_to_wgpu_helper<'a>(
|
fn markup_to_wgpu_helper<'a>(
|
||||||
markup_node: &'a MarkupNode,
|
markup_node: &'a MarkupNode,
|
||||||
wgpu_texts: &mut Vec<glyph_brush::OwnedText>,
|
wgpu_texts: &mut Vec<glyph_brush::OwnedText>,
|
||||||
rects: &mut Vec<Rect>,
|
rects: &mut Vec<Rect>,
|
||||||
code_style: &CodeStyle,
|
code_style: &CodeStyle,
|
||||||
txt_row_col: &mut (usize, usize),
|
txt_row_col: &mut (usize, usize),
|
||||||
markup_node_pool: &'a SlowPool,
|
mark_node_pool: &'a SlowPool,
|
||||||
) -> EdResult<()> {
|
) -> EdResult<()> {
|
||||||
match markup_node {
|
match markup_node {
|
||||||
MarkupNode::Nested {
|
MarkupNode::Nested {
|
||||||
ast_node_id: _,
|
ast_node_id: _,
|
||||||
children_ids,
|
children_ids,
|
||||||
parent_id_opt: _,
|
parent_id_opt: _,
|
||||||
|
newlines_at_end,
|
||||||
} => {
|
} => {
|
||||||
for child_id in children_ids.iter() {
|
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(
|
markup_to_wgpu_helper(
|
||||||
child,
|
child,
|
||||||
wgpu_texts,
|
wgpu_texts,
|
||||||
rects,
|
rects,
|
||||||
code_style,
|
code_style,
|
||||||
txt_row_col,
|
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 {
|
MarkupNode::Text {
|
||||||
content,
|
content,
|
||||||
|
@ -107,14 +126,23 @@ fn markup_to_wgpu_helper<'a>(
|
||||||
syn_high_style,
|
syn_high_style,
|
||||||
attributes: _,
|
attributes: _,
|
||||||
parent_id_opt: _,
|
parent_id_opt: _,
|
||||||
|
newlines_at_end,
|
||||||
} => {
|
} => {
|
||||||
let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, syn_high_style)?;
|
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_color(colors::to_slice(*highlight_color))
|
||||||
.with_scale(code_style.font_size);
|
.with_scale(code_style.font_size);
|
||||||
|
|
||||||
txt_row_col.1 += content.len();
|
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);
|
wgpu_texts.push(glyph_text);
|
||||||
}
|
}
|
||||||
MarkupNode::Blank {
|
MarkupNode::Blank {
|
||||||
|
@ -122,8 +150,11 @@ fn markup_to_wgpu_helper<'a>(
|
||||||
attributes: _,
|
attributes: _,
|
||||||
syn_high_style,
|
syn_high_style,
|
||||||
parent_id_opt: _,
|
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_color(colors::to_slice(colors::WHITE))
|
||||||
.with_scale(code_style.font_size);
|
.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_width = code_style.glyph_dim_rect.width;
|
||||||
let char_height = code_style.glyph_dim_rect.height;
|
let char_height = code_style.glyph_dim_rect.height;
|
||||||
|
|
||||||
let hole_rect = Rect {
|
let blank_rect = Rect {
|
||||||
top_left_coords: (
|
top_left_coords: (
|
||||||
code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width,
|
code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width,
|
||||||
code_style.txt_coords.y
|
code_style.txt_coords.y
|
||||||
|
@ -144,12 +175,21 @@ fn markup_to_wgpu_helper<'a>(
|
||||||
height: char_height,
|
height: char_height,
|
||||||
color: *highlight_color,
|
color: *highlight_color,
|
||||||
};
|
};
|
||||||
rects.push(hole_rect);
|
rects.push(blank_rect);
|
||||||
|
|
||||||
txt_row_col.1 += BLANK_PLACEHOLDER.len();
|
txt_row_col.1 += BLANK_PLACEHOLDER.len();
|
||||||
wgpu_texts.push(glyph_text);
|
wgpu_texts.push(glyph_text);
|
||||||
|
|
||||||
|
for _ in 0..*newlines_at_end {
|
||||||
|
txt_row_col.0 += 1;
|
||||||
|
txt_row_col.1 = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
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;
|
||||||
use crate::graphics::colors::from_hsb;
|
use crate::graphics::colors::from_hsb;
|
||||||
use crate::graphics::primitives::text as gr_text;
|
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 cgmath::Vector2;
|
||||||
use winit::dpi::PhysicalSize;
|
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 area_bounds = (size.width as f32, size.height as f32);
|
||||||
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
|
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))
|
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_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))
|
let code_lines_text = glyph_brush::OwnedText::new(format!("{}", ed_model.code_lines))
|
||||||
.with_color(colors::to_slice(from_hsb(0, 49, 96)))
|
.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(
|
let mut mark_node_trees_string = "\nmark node trees:".to_owned();
|
||||||
ed_model.markup_root_id,
|
|
||||||
&ed_model.markup_node_pool,
|
|
||||||
))
|
|
||||||
.with_color(colors::to_slice(from_hsb(266, 31, 96)))
|
|
||||||
.with_scale(config.code_font_size);
|
|
||||||
|
|
||||||
let mark_node_pool_text = glyph_brush::OwnedText::new(format!("{}", ed_model.markup_node_pool))
|
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.debug_font_size);
|
||||||
|
|
||||||
|
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_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!(
|
let mut ast_node_text_str = "AST:\n".to_owned();
|
||||||
"\n\n(ast_root)\n{}",
|
|
||||||
expr2_to_string(ed_model.module.ast_root_id, ed_model.module.env.pool)
|
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))
|
||||||
.with_color(colors::to_slice(from_hsb(211, 80, 100)))
|
}
|
||||||
.with_scale(config.code_font_size);
|
|
||||||
|
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.debug_font_size);
|
||||||
|
|
||||||
let section = gr_text::owned_section_from_glyph_texts(
|
let section = gr_text::owned_section_from_glyph_texts(
|
||||||
vec![
|
vec![
|
||||||
|
carets_text,
|
||||||
grid_node_map_text,
|
grid_node_map_text,
|
||||||
code_lines_text,
|
code_lines_text,
|
||||||
mark_node_tree_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.";
|
#![allow(dead_code)]
|
||||||
pub const START_TIP: &str =
|
|
||||||
"Start by typing '[', '{', '\"' or a number.\nInput chars that would create parse errors will be ignored.";
|
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 {:?} {}",
|
"{}: {} ({}) ast_id {:?} {}",
|
||||||
index,
|
index,
|
||||||
node.node_type_as_string(),
|
node.node_type_as_string(),
|
||||||
node.get_content().unwrap_or_else(|_| "".to_string()),
|
node.get_content(),
|
||||||
ast_node_id.parse::<usize>().unwrap(),
|
ast_node_id.parse::<usize>().unwrap(),
|
||||||
child_str
|
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...
|
PackageRelated, // app, packages, imports, exposes, provides...
|
||||||
Variable,
|
Variable,
|
||||||
RecordField,
|
RecordField,
|
||||||
|
Import,
|
||||||
|
Provides,
|
||||||
Blank,
|
Blank,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +33,8 @@ pub fn default_highlight_map() -> HashMap<HighlightStyle, RgbaTup> {
|
||||||
(PackageRelated, gr_colors::WHITE),
|
(PackageRelated, gr_colors::WHITE),
|
||||||
(Variable, gr_colors::WHITE),
|
(Variable, gr_colors::WHITE),
|
||||||
(RecordField, from_hsb(258, 50, 90)),
|
(RecordField, from_hsb(258, 50, 90)),
|
||||||
|
(Import, from_hsb(185, 50, 75)),
|
||||||
|
(Provides, from_hsb(185, 50, 75)),
|
||||||
(Blank, from_hsb(258, 50, 90)),
|
(Blank, from_hsb(258, 50, 90)),
|
||||||
// comment from_hsb(285, 6, 47) or 186, 35, 40
|
// comment from_hsb(285, 6, 47) or 186, 35, 40
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
|
use crate::editor::ed_error::{EdResult, UnexpectedASTNode};
|
||||||
use crate::lang::pattern::{Pattern2, PatternId};
|
use crate::lang::pattern::{Pattern2, PatternId};
|
||||||
use crate::lang::pool::Pool;
|
use crate::lang::pool::Pool;
|
||||||
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
|
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 */),
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum ValueDef {
|
pub enum ValueDef {
|
||||||
WithAnnotation {
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum FunctionDef {
|
pub enum FunctionDef {
|
||||||
WithAnnotation {
|
WithAnnotation {
|
||||||
|
@ -402,7 +456,11 @@ pub struct WhenBranch {
|
||||||
// TODO make the inner types private?
|
// TODO make the inner types private?
|
||||||
pub type ExprId = NodeId<Expr2>;
|
pub type ExprId = NodeId<Expr2>;
|
||||||
|
|
||||||
|
pub type DefId = NodeId<Def2>;
|
||||||
|
|
||||||
use RecordField::*;
|
use RecordField::*;
|
||||||
|
|
||||||
|
use super::parse::ASTNodeId;
|
||||||
impl RecordField {
|
impl RecordField {
|
||||||
pub fn get_record_field_var(&self) -> &Variable {
|
pub fn get_record_field_var(&self) -> &Variable {
|
||||||
match self {
|
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 {
|
pub fn expr2_to_string(node_id: ExprId, pool: &Pool) -> String {
|
||||||
let mut full_string = String::new();
|
let mut full_string = String::new();
|
||||||
let expr2 = pool.get(node_id);
|
let expr2 = pool.get(node_id);
|
||||||
|
@ -550,16 +615,118 @@ fn expr2_to_string_helper(
|
||||||
Expr2::SmallInt { text, .. } => {
|
Expr2::SmallInt { text, .. } => {
|
||||||
out_string.push_str(&format!("SmallInt({})", text.as_str(pool)));
|
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),
|
other => todo!("Implement for {:?}", other),
|
||||||
}
|
}
|
||||||
|
|
||||||
out_string.push('\n');
|
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 {
|
fn var_to_string(some_var: &Variable, indent_level: usize) -> String {
|
||||||
format!("{}Var({:?})\n", get_spacing(indent_level + 1), some_var)
|
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]
|
#[test]
|
||||||
fn size_of_expr() {
|
fn size_of_expr() {
|
||||||
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);
|
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use crate::lang::ast::{
|
use crate::lang::ast::{
|
||||||
expr2_to_string, ClosureExtra, Expr2, ExprId, FloatVal, IntStyle, IntVal, RecordField,
|
expr2_to_string, value_def_to_string, ClosureExtra, Def2, Expr2, ExprId, FloatVal, IntStyle,
|
||||||
WhenBranch,
|
IntVal, RecordField, ValueDef, WhenBranch,
|
||||||
};
|
};
|
||||||
use crate::lang::def::{
|
use crate::lang::def::{
|
||||||
canonicalize_defs, sort_can_defs, CanDefs, Declaration, Def, PendingDef, References,
|
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)
|
(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>(
|
pub fn str_to_expr2<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
|
@ -296,20 +316,23 @@ pub fn str_to_expr2<'a>(
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Result<(Expr2, self::Output), SyntaxError<'a>> {
|
) -> Result<(Expr2, self::Output), SyntaxError<'a>> {
|
||||||
match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) {
|
match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) {
|
||||||
Ok(loc_expr) => {
|
Ok(loc_expr) => Ok(loc_expr_to_expr2(arena, loc_expr, env, scope, region)),
|
||||||
let desugared_loc_expr = desugar_expr(arena, arena.alloc(loc_expr));
|
|
||||||
|
|
||||||
Ok(to_expr2(
|
|
||||||
env,
|
|
||||||
scope,
|
|
||||||
arena.alloc(desugared_loc_expr.value),
|
|
||||||
region,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Err(fail) => Err(fail),
|
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>(
|
pub fn to_expr2<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
scope: &mut Scope,
|
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>(
|
fn flatten_str_literal<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
scope: &mut Scope,
|
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::AnnotationOnly { .. } => todo!(),
|
||||||
Def::Value(value_def) => {
|
Def::Value(value_def) => {
|
||||||
let def_id = pool.add(value_def);
|
let def_id = pool.add(value_def);
|
||||||
|
|
||||||
let body_id = pool.add(ret);
|
let body_id = pool.add(ret);
|
||||||
|
|
||||||
Expr2::LetValue {
|
Expr2::LetValue {
|
||||||
|
|
|
@ -3,7 +3,8 @@ pub mod constrain;
|
||||||
mod def;
|
mod def;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
mod module;
|
mod module;
|
||||||
mod pattern;
|
pub mod parse;
|
||||||
|
pub mod pattern;
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
pub mod roc_file;
|
pub mod roc_file;
|
||||||
pub mod scope;
|
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(clippy::all)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
use crate::editor::ed_error::{EdResult, UnexpectedPattern2Variant};
|
||||||
use crate::lang::ast::{ExprId, FloatVal, IntVal};
|
use crate::lang::ast::{ExprId, FloatVal, IntVal};
|
||||||
use crate::lang::expr::{to_expr_id, Env, Output};
|
use crate::lang::expr::{to_expr_id, Env, Output};
|
||||||
use crate::lang::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
|
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::expr::unescape_char;
|
||||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
||||||
use roc_collections::all::BumpMap;
|
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::ast::{StrLiteral, StrSegment};
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||||
|
@ -482,6 +483,17 @@ pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
||||||
symbols
|
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(
|
pub fn symbols_and_variables_from_pattern(
|
||||||
pool: &Pool,
|
pool: &Pool,
|
||||||
initial: &Pattern2,
|
initial: &Pattern2,
|
||||||
|
|
|
@ -17,6 +17,6 @@ mod window;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn launch(filepaths: &[&Path]) -> io::Result<()> {
|
pub fn launch(project_dir_path_opt: Option<&Path>) -> io::Result<()> {
|
||||||
editor::main::launch(filepaths)
|
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 selection_opt: Option<Selection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum CaretPos {
|
||||||
|
Start,
|
||||||
|
Exact(TextPos),
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
fn mk_some_sel(start_pos: TextPos, end_pos: TextPos) -> UIResult<Option<Selection>> {
|
fn mk_some_sel(start_pos: TextPos, end_pos: TextPos) -> UIResult<Option<Selection>> {
|
||||||
if start_pos == end_pos {
|
if start_pos == end_pos {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -146,7 +152,7 @@ pub mod test_caret_w_select {
|
||||||
|
|
||||||
// Retrieve selection and position from formatted string
|
// Retrieve selection and position from formatted string
|
||||||
pub fn convert_dsl_to_selection(lines: &[String]) -> Result<CaretWSelect, 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)
|
let parsed = LineParser::parse(Rule::linesWithSelect, &lines_str)
|
||||||
.expect("Selection test DSL parsing failed");
|
.expect("Selection test DSL parsing failed");
|
||||||
|
|
|
@ -13,14 +13,12 @@ use crate::ui::text::{
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
use crate::ui::util::is_newline;
|
use crate::ui::util::is_newline;
|
||||||
use crate::window::keyboard_input::Modifiers;
|
use crate::window::keyboard_input::Modifiers;
|
||||||
use bumpalo::collections::String as BumpString;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use winit::event::VirtualKeyCode;
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
pub trait Lines {
|
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>;
|
fn line_len(&self, line_nr: usize) -> UIResult<usize>;
|
||||||
|
|
||||||
|
@ -28,7 +26,7 @@ pub trait Lines {
|
||||||
|
|
||||||
fn nr_of_chars(&self) -> usize;
|
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;
|
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 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<()>;
|
fn del_selection(&mut self) -> UIResult<()>;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +112,7 @@ pub fn move_caret_left<T: Lines>(
|
||||||
} else {
|
} else {
|
||||||
let curr_line_len = lines.line_len(old_line_nr - 1)?;
|
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 {
|
} else {
|
||||||
(old_line_nr, old_col_nr - 1)
|
(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);
|
let is_last_line = lines.is_last_line(old_line_nr);
|
||||||
|
|
||||||
if !is_last_line {
|
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)
|
(old_line_nr + 1, 0)
|
||||||
} else {
|
} else {
|
||||||
(old_line_nr, old_col_nr + 1)
|
(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)?;
|
let prev_line_len = lines.line_len(old_line_nr - 1)?;
|
||||||
|
|
||||||
if prev_line_len <= old_col_nr {
|
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 {
|
} else {
|
||||||
(old_line_nr - 1, old_col_nr)
|
(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 next_line_len <= old_col_nr {
|
||||||
if !is_last_line {
|
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 {
|
} else {
|
||||||
(old_line_nr + 1, next_line_len)
|
(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 curr_line_nr = caret_w_select.caret_pos.line;
|
||||||
let old_col_nr = caret_w_select.caret_pos.column;
|
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 line_char_iter = curr_line_str.chars();
|
||||||
|
|
||||||
let mut first_no_space_char_col = 0;
|
let mut first_no_space_char_col = 0;
|
||||||
|
|
|
@ -2,4 +2,5 @@ pub mod big_text_area;
|
||||||
pub mod caret_w_select;
|
pub mod caret_w_select;
|
||||||
pub mod lines;
|
pub mod lines;
|
||||||
pub mod selection;
|
pub mod selection;
|
||||||
|
mod text_buffer;
|
||||||
pub mod text_pos;
|
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};
|
use snafu::{Backtrace, Snafu};
|
||||||
|
|
||||||
//import errors as follows:
|
//import errors as follows:
|
||||||
|
@ -8,6 +10,16 @@ use snafu::{Backtrace, Snafu};
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
#[snafu(visibility(pub))]
|
#[snafu(visibility(pub))]
|
||||||
pub enum UIError {
|
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(
|
#[snafu(display(
|
||||||
"OutOfBounds: index {} was out of bounds for {} with length {}.",
|
"OutOfBounds: index {} was out of bounds for {} with length {}.",
|
||||||
index,
|
index,
|
||||||
|
@ -34,6 +46,13 @@ pub enum UIError {
|
||||||
))]
|
))]
|
||||||
FileOpenFailed { path_str: String, err_msg: String },
|
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))]
|
#[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 },
|
TextBufReadFailed { path_str: String, err_msg: String },
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::ui_error::{OutOfBounds, UIResult};
|
use super::ui_error::{FileOpenFailed, FileWriteFailed, OutOfBounds, UIResult};
|
||||||
use snafu::OptionExt;
|
use snafu::{OptionExt, ResultExt};
|
||||||
use std::slice::SliceIndex;
|
use std::{fs::File, io::BufReader, path::Path, slice::SliceIndex};
|
||||||
|
|
||||||
pub fn is_newline(char_ref: &char) -> bool {
|
pub fn is_newline(char_ref: &char) -> bool {
|
||||||
let newline_codes = vec!['\u{d}', '\n'];
|
let newline_codes = vec!['\u{d}', '\n'];
|
||||||
|
@ -33,3 +33,27 @@ pub fn slice_get_mut<T>(
|
||||||
|
|
||||||
Ok(elt_ref)
|
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
|
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 {
|
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 = {"┃"}
|
caret = {"┃"}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue